import * as vscode from 'vscode'; import * as fspath from 'path'; import * as fs from 'fs'; import { AbsPath, opeParam } from '../global'; import { PrjInfo, RawPrjInfo } from '../global/prjInfo'; import { HdlLangID } from '../global/enum'; import { hdlFile, hdlPath } from '../hdlFs'; import { getIconConfig } from '../hdlFs/icons'; import { getLanguageId } from '../hdlFs/file'; type MissPathType = { path?: string }; type LibPickItem = vscode.QuickPickItem & MissPathType; class LibPick { commonPath: AbsPath; customPath: AbsPath; commonQuickPickItem: LibPickItem; customQuickPickItem: LibPickItem; rootItems: LibPickItem[]; backQuickPickItem: LibPickItem; curPath: AbsPath; selectedQuickPickItem: LibPickItem | undefined; constructor () { this.commonPath = opeParam.prjInfo.libCommonPath; this.customPath = opeParam.prjInfo.libCustomPath; if (!this.customPath) { this.customPath = 'no custom path is defined, see Prj->Lib->Custom->Path'; } this.commonQuickPickItem = { label: "$(libpick-common) common", description: 'common library provided by us', detail: 'current path: ' + this.commonPath, path: this.commonPath, buttons: [{iconPath: getIconConfig('import'), tooltip: 'import everything in common'}] }; this.customQuickPickItem = { label: "$(libpick-custom) custom", description: 'custom library by yourself', detail: 'current path: ' + this.customPath, path: this.customPath, buttons: [{iconPath: getIconConfig('import'), tooltip: 'import everything in custom'}] }; this.rootItems = [ this.commonQuickPickItem, this.customQuickPickItem ]; this.backQuickPickItem = { label: '...', description: 'return' }; this.curPath = ''; } getPathIcon(path: AbsPath): string { let prompt; if (hdlFile.isFile(path)) { const langID = hdlFile.getLanguageId(path); if (langID === HdlLangID.Vhdl) { prompt = 'vhdl'; } else if (langID === HdlLangID.Verilog || langID === HdlLangID.SystemVerilog) { prompt = 'verilog'; } else { prompt = 'unknown'; } } else { prompt = 'folder'; } return `$(libpick-${prompt})`; } private getReadmeText(path: AbsPath, fileName: string): string | undefined { const allowReadmeFile = ['readme.md', 'README.md', 'readme', 'README', 'readme.txt'] for (const readmeFile of allowReadmeFile) { const mdpath = hdlPath.join(path, fileName, readmeFile); if (fs.existsSync(mdpath)) { return hdlFile.readFile(mdpath); } } return undefined; } private makeQuickPickItemsByPath(path: AbsPath, back: boolean=true): LibPickItem[] { const items: LibPickItem[] = []; if (!hdlPath.exist(path)) { return items; } if (back) { items.push(this.backQuickPickItem); } for (const fileName of fs.readdirSync(path)) { const filePath = hdlPath.join(path, fileName); if (hdlFile.isFile(filePath)) { const fileLangId = getLanguageId(filePath); if (fileLangId === HdlLangID.Unknown) { // 不是 hdl 直接跳过 continue; } const themeIcon = this.getPathIcon(filePath); const label = themeIcon + " " + fileName; const buttons = [{iconPath: getIconConfig('import'), tooltip: 'import everything in ' + fileName}]; items.push({label, description: '', path: filePath, buttons}); } else if (hdlFile.isDir(filePath)) { if (['.git', '.github', '.vscode'].includes(fileName)) { continue; } const themeIcon = this.getPathIcon(filePath); const label = themeIcon + " " + fileName; // 寻找 fileName 下的 readme,fileName 在这里是一个 文件夹 const mdText = this.getReadmeText(path, fileName); const description = mdText ? mdText : ''; const buttons = [{iconPath: getIconConfig('import'), tooltip: 'import everything in ' + fileName}]; items.push({label, description, path: filePath, buttons}); } } return items; } private provideQuickPickItem(item?: LibPickItem) { if (!item) { return this.rootItems; } else if (item === this.backQuickPickItem) { if ((this.curPath === this.commonPath) || (this.curPath === this.customPath)) { return this.rootItems; } else { // rollback the current path this.curPath = fspath.dirname(this.curPath); } } else if (item === this.commonQuickPickItem) { this.curPath = this.commonPath; } else if (item === this.customQuickPickItem) { this.curPath = this.customPath; } else { const label = item.label; const fileName = label.replace(/\$\([\s\S]*\)/, '').trim(); this.curPath = hdlPath.join(this.curPath, fileName); } return this.makeQuickPickItemsByPath(this.curPath); } async pickItems() { const pickWidget = vscode.window.createQuickPick(); pickWidget.placeholder = 'pick the library'; pickWidget.items = this.provideQuickPickItem(); pickWidget.onDidChangeSelection(items => { if (items[0]) { this.selectedQuickPickItem = items[0]; } }); pickWidget.onDidAccept(() => { if (this.selectedQuickPickItem) { const childernItems = this.provideQuickPickItem(this.selectedQuickPickItem); if (childernItems && childernItems.length > 0) { pickWidget.items = childernItems; } } }); pickWidget.onDidTriggerItemButton(event => { const selectedPath = event.item.path; if (selectedPath && hdlPath.exist(selectedPath)) { const userPrjInfo = opeParam.getUserPrjInfo(); if (selectedPath.includes(this.commonQuickPickItem.path!)) { // this is a module import from common, use relative path const relPath = selectedPath.replace(this.commonQuickPickItem.path + '/', ''); userPrjInfo.appendLibraryCommonPath(relPath); } else { // this is a module import from custom, use absolute path const relPath = selectedPath.replace(this.customQuickPickItem.path + '/', ''); userPrjInfo.appendLibraryCustomPath(relPath); } // acquire raw and replace it const rawUserPrjInfo = opeParam.getRawUserPrjInfo(); rawUserPrjInfo.library = userPrjInfo.library; hdlFile.writeJSON(opeParam.propertyJsonPath, rawUserPrjInfo); } }); pickWidget.show(); } } async function pickLibrary() { const picker = new LibPick(); await picker.pickItems(); } export { LibPick, LibPickItem, pickLibrary };