diff --git a/draft2.json b/format.json similarity index 99% rename from draft2.json rename to format.json index b719be3..8220f73 100644 --- a/draft2.json +++ b/format.json @@ -1,6 +1,5 @@ { "content": { - "error": [], "modules": [ { "instances": [ @@ -180,7 +179,6 @@ "value": "out" } ], - "error": [], "includes": [ { "path": "child_1.v", diff --git a/package.json b/package.json index 04ea114..f644dc0 100644 --- a/package.json +++ b/package.json @@ -24,12 +24,18 @@ "configuration": { "title": "Digital-IDE", "properties": { - "lib.custom.path": { + "prj.lib.custom.path": { "scope": "window", "type": "string", "default": "", "description": "path of the dictionary of \"custom\" in library" }, + "prj.file.structure.notice": { + "scope": "window", + "type": "boolean", + "default": true, + "description": "notice when change file structure" + }, "function.doc.webview.backgroundImage": { "type": "string", "default": "", @@ -168,6 +174,15 @@ "dark": "images/svg/dark/debug.svg" } }, + { + "command": "digital-ide.pickLibrary", + "title": "import library", + "icon": { + "light": "images/svg/light/library.svg", + "dark": "images/svg/dark/library.svg" + }, + "category": "Digital-IDE" + }, { "command": "digital-ide.treeView.arch.expand", "category": "tool", @@ -280,7 +295,7 @@ "when": "view == digital-ide-treeView-arch" }, { - "command": "TOOL.libPick", + "command": "digital-ide.pickLibrary", "group": "navigation", "when": "view == digital-ide-treeView-arch" }, diff --git a/src/extension.ts b/src/extension.ts index 3395309..91b7703 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -14,8 +14,10 @@ async function launch(context: vscode.ExtensionContext) { console.time('launch'); prjManage.initOpeParam(context); console.log(opeParam.prjInfo); + const hdlFiles = prjManage.getPrjHardwareFiles(); await hdlParam.initialize(hdlFiles); + console.timeLog('launch'); await registerCommand(context); diff --git a/src/global/opeParam.ts b/src/global/opeParam.ts index c6e40b8..e57a06a 100644 --- a/src/global/opeParam.ts +++ b/src/global/opeParam.ts @@ -1,6 +1,5 @@ import * as assert from 'assert'; import * as fs from 'fs'; -import { hdlFile } from '../hdlFs'; import { Arch, PrjInfo, RawPrjInfo, resolve, toSlash } from './prjInfo'; @@ -24,6 +23,16 @@ interface FirstTopModuleDesc { path: AbsPath }; +function readJSON(path: AbsPath): object { + try { + const context = fs.readFileSync(path, 'utf-8'); + return JSON.parse(context); + } catch (err) { + console.log('fail to read JSON: ', err); + } + return {}; +} + class OpeParam { private _os: string = OpeParamDefaults.os; @@ -146,18 +155,32 @@ class OpeParam { /** * get User's property.json */ - public getUserPrjInfo() { + public getUserPrjInfo(): PrjInfo { const propertyJsonPath = this.propertyJsonPath; const userPrjInfo = new PrjInfo(); if (fs.existsSync(propertyJsonPath)) { - const rawPrjInfo = hdlFile.readJSON(propertyJsonPath); + const rawPrjInfo = readJSON(propertyJsonPath); userPrjInfo.merge(rawPrjInfo); } else { // use default config instead - const rawPrjInfo = hdlFile.readJSON(this.propertyInitPath); + const rawPrjInfo = readJSON(this.propertyInitPath); userPrjInfo.merge(rawPrjInfo); } + return userPrjInfo; } + + public getRawUserPrjInfo(): RawPrjInfo { + const propertyJsonPath = this.propertyJsonPath; + if (fs.existsSync(propertyJsonPath)) { + const rawPrjInfo = readJSON(propertyJsonPath); + return rawPrjInfo; + } else { + // use default config instead + const rawPrjInfo = readJSON(this.propertyInitPath); + return rawPrjInfo; + } + } + }; const opeParam: OpeParam = new OpeParam(); diff --git a/src/global/prjInfo.ts b/src/global/prjInfo.ts index 7b972d2..67a338e 100644 --- a/src/global/prjInfo.ts +++ b/src/global/prjInfo.ts @@ -8,6 +8,8 @@ import { ToolChainType, LibraryState, XilinxIP, validToolChainType, validXilinxIP, validLibraryState } from './enum'; import { PrjInfoSchema } from './propertySchema'; import assert = require('assert'); +import * as hdlPath from '../hdlFs/path'; + type AbsPath = string; type RelPath = string; @@ -409,7 +411,7 @@ class PrjInfo implements PrjInfoMeta { } } if (library.hardware) { - // TODO : finish this when you can require root of common and custom + // TODO : finish this when you can acquire root of common and custom const commonPath = this.libCommonPath; const customPath = this.libCustomPath; this.updatePathWisely(this.library.hardware, 'common', library.hardware.common, commonPath); @@ -417,7 +419,31 @@ class PrjInfo implements PrjInfoMeta { } } } - + + public appendLibraryCommonPath(relPath: RelPath) { + this._library.hardware.common.push(relPath); + } + + public appendLibraryCustomPath(relPath: RelPath) { + this._library.hardware.custom.push(relPath); + } + + public getLibraryCommonPaths(absolute: boolean = true): Path[] { + if (absolute) { + const commonFolder = hdlPath.join(this.libCommonPath, 'Empty'); + return this._library.hardware.common.map(relPath => hdlPath.rel2abs(commonFolder, relPath)); + } + return this._library.hardware.common; + } + + public getLibraryCustomPaths(absolute: boolean = true): Path[] { + if (absolute) { + const configFolder = hdlPath.join(this.libCustomPath, 'Empty'); + return this._library.hardware.custom.map(relPath => hdlPath.rel2abs(configFolder, relPath)); + } + return this._library.hardware.custom; + } + /** * merge the input uncomplete prjInfo into this * cover the value that exist in rawPrjInfo recursively @@ -450,7 +476,20 @@ class PrjInfo implements PrjInfoMeta { } public get libCustomPath(): AbsPath { - return vscode.workspace.getConfiguration().get('lib.custom.path', this._workspacePath); + return vscode.workspace.getConfiguration().get('prj.lib.custom.path', this._workspacePath); + } + + public json(): RawPrjInfo { + return { + toolChain: this._toolChain, + prjName: this._prjName, + IP_REPO: this._IP_REPO, + soc: this._soc, + enableShowLog: this._enableShowLog, + device: this._device, + arch: this._arch, + library: this._library, + }; } }; diff --git a/src/global/util.ts b/src/global/util.ts index a9cd2fa..0316e85 100644 --- a/src/global/util.ts +++ b/src/global/util.ts @@ -11,7 +11,7 @@ class PathSet { if (path instanceof Array) { path.forEach(p => this.checkAdd(p)); } else if (fs.existsSync(path)) { - this.files.add(path); + this.files.add(path); } } } diff --git a/src/hdlFs/file.ts b/src/hdlFs/file.ts index d6988e3..4186767 100644 --- a/src/hdlFs/file.ts +++ b/src/hdlFs/file.ts @@ -74,7 +74,7 @@ function isHDLFile(path: AbsPath): boolean { } -function getHDLFiles(path: AbsPath | AbsPath[] | Set, ignores?: AbsPath[]) { +function getHDLFiles(path: AbsPath | AbsPath[] | Set, ignores?: AbsPath[]): AbsPath[] { return pickFileRecursive(path, ignores, filePath => isHDLFile(filePath)); } @@ -98,7 +98,7 @@ function pickFileRecursive(path: AbsPath | AbsPath[] | Set, ignores?: A for (const file of fs.readdirSync(path)) { const filePath = hdlPath.join(path, file); if (isDir(filePath)) { - const subHdlFiles = getHDLFiles(filePath, ignores); + const subHdlFiles = pickFileRecursive(filePath, ignores); if (subHdlFiles.length > 0) { hdlFiles.push(...subHdlFiles); } diff --git a/src/manager/index.ts b/src/manager/index.ts index 38716b7..3a6e596 100644 --- a/src/manager/index.ts +++ b/src/manager/index.ts @@ -1,11 +1,17 @@ import * as vscode from 'vscode'; import { prjManage } from './prj'; +import { pickLibrary } from './libPick'; function registerManagerCommands(context: vscode.ExtensionContext) { vscode.commands.registerCommand('digital-ide.property-json.generate', prjManage.generatePropertyJson); vscode.commands.registerCommand('digital-ide.property-json.overwrite', prjManage.overwritePropertyJson); + + // libpick + vscode.commands.registerCommand('digital-ide.pickLibrary', pickLibrary); + + } export { diff --git a/src/manager/lib.ts b/src/manager/lib.ts index a35231e..5fd5a1d 100644 --- a/src/manager/lib.ts +++ b/src/manager/lib.ts @@ -1,11 +1,50 @@ import * as vscode from 'vscode'; +import * as fs from 'fs'; import { AbsPath, opeParam } from '../global'; -import { hdlFile, hdlPath } from '../hdlFs'; +import { hdlDir, hdlFile, hdlPath } from '../hdlFs'; +import { Library } from '../global/prjInfo'; +import { Path } from '../../resources/hdlParser'; + +interface LibFileChange { + add: AbsPath[], + del: AbsPath[], +} + +interface LibStatus { + type?: string, + list: AbsPath[] +} + + +/** + * a与b的差集 + * @param a + * @param b + * @returns + */ +function diffElement(a: T[], b: T[]): T[] { + const bSet = new Set(b); + return a.filter(el => !bSet.has(el)); +} + + +function removeDuplicates(a: T[]): T[] { + const aSet = new Set(a); + return [...aSet]; +} class LibManage { - constructor() { + curr: LibStatus; + next: LibStatus; + constructor() { + this.curr = { list : [] }; + this.next = { list : [] }; + } + + public get customerPath(): AbsPath { + return opeParam.prjInfo.libCustomPath; } public get srcPath(): AbsPath { @@ -32,8 +71,118 @@ class LibManage { return opeParam.prjInfo.libCustomPath; } - public processLibFiles() { - + public processLibFiles(library: Library): LibFileChange { + // 在不设置state属性的时候默认为remote + this.next.list = this.getLibFiles(library); + if (!hdlFile.isHasAttr(library, 'state')) { + this.next.type = 'remote'; + } else { + if (library.state !== 'remote' && library.state !== 'local') { + return { + 'add' : [], + 'del' : [], + }; + } + this.next.type = library.state; + } + + // 处于初始状态时的情况 + if (!this.curr.type) { + if (!hdlFile.isDir(this.localLibPath)) { + this.curr.type = 'local'; + } else { + this.curr.type = 'remote'; + } + } + + const state = `${this.curr.type}-${this.next.type}`; + let add: AbsPath[] = []; + let del: AbsPath[] = []; + switch (state) { + case 'remote-remote': + add = diffElement(this.next.list, this.curr.list); + del = diffElement(this.curr.list, this.next.list); + break; + case 'remote-local': + // 删除的内容全是remote的,将curr的交出去即可 + del = this.curr.list; + + // 将新增的全部复制到本地,交给monitor进行处理 + this.remote2Local(this.next.list, (src, dist) => { + hdlFile.copyFile(src, dist); + }); + break; + case 'local-remote': + // 本地的lib全部删除,交给monitor进行处理 + const fn = async () => { + if (fs.existsSync(this.localLibPath)) { + const needNotice = vscode.workspace.getConfiguration('prj.file.structure.notice'); + if (needNotice) { + let select = await vscode.window.showWarningMessage("local lib will be removed.", 'Yes', 'Cancel'); + if (select === "Yes") { + hdlDir.rmdir(this.localLibPath); + } + } else { + hdlDir.rmdir(this.localLibPath); + } + } + }; + fn(); + + // 增加的内容全是remote的,将next的交出去即可 + add = this.next.list; + break; + case 'local-local': + // 只管理library里面的内容,如果自己再localPath里加减代码,则不去管理 + add = diffElement(this.next.list, this.curr.list); + del = diffElement(this.curr.list, this.next.list); + + this.remote2Local(add, (src, dist) => { + hdlFile.copyFile(src, dist); + }); + + this.remote2Local(del, (src, dist) => { + hdlFile.removeFile(dist); + }); + add = []; del = []; + break; + default: break; + } + + return { add, del }; } + + getLibFiles(library: Library) { + const libFileList: AbsPath[] = []; + const prjInfo = opeParam.prjInfo; + + // collect common libs + prjInfo.getLibraryCommonPaths().forEach(absPath => libFileList.push(...hdlFile.getHDLFiles(absPath))); + + // collect custom libs + prjInfo.getLibraryCustomPaths().forEach(absPath => libFileList.push(...hdlFile.getHDLFiles(absPath))); + + // Remove duplicate HDL files + return removeDuplicates(libFileList); + } + + remote2Local(remotes: Path[], callback: (src: AbsPath, dist: AbsPath) => void) { + for (const src of remotes) { + let dist; + if (src.includes(this.customerPath)) { + dist = src.replace(this.customerPath, this.localLibPath); + } else { + dist = src.replace(this.sourceLibPath, this.localLibPath); + } + callback(src, dist); + } + } +}; + +const libManage = new LibManage(); + +export { + libManage, + LibManage }; \ No newline at end of file diff --git a/src/manager/libPick.ts b/src/manager/libPick.ts index 62458b8..e606db6 100644 --- a/src/manager/libPick.ts +++ b/src/manager/libPick.ts @@ -3,8 +3,9 @@ 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 { hdlDir, hdlFile, hdlPath } from '../hdlFs'; +import { hdlFile, hdlPath } from '../hdlFs'; import { getIconConfig } from '../hdlFs/icons'; type MissPathType = { path?: string }; @@ -144,30 +145,35 @@ class LibPick { const selectedPath = event.item.path; if (selectedPath && hdlPath.exist(selectedPath)) { - const ppyPath = hdlPath.join(opeParam.workspacePath, '.vscode', 'property.json'); - - // 抽象这块 - let prjInfo = null; - // 如果存在,则读取用户的配置文件,否则使用默认的 - if (!hdlPath.exist(ppyPath)) { - prjInfo = hdlFile.readJSON(opeParam.propertyInitPath); - } else { - prjInfo = hdlFile.readJSON(ppyPath); - } - + 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 + '/', ''); - appendLibraryCommonPath(relPath, prjInfo); + 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 + '/', ''); - appendLibraryCustomPath(relPath, prjInfo); + userPrjInfo.appendLibraryCustomPath(relPath); } - hdlFile.writeJSON(ppyPath, prjInfo); + + // acquire raw and replace it + const rawUserPrjInfo = opeParam.getRawUserPrjInfo(); + rawUserPrjInfo.library = userPrjInfo.library; + hdlFile.writeJSON(opeParam.propertyJsonPath, rawUserPrjInfo); } }); pickWidget.show(); } -} \ No newline at end of file +} + +function pickLibrary() { + const picker = new LibPick(); + picker.pickItems(); +} + +export { + LibPick, + LibPickItem, + pickLibrary +}; \ No newline at end of file diff --git a/src/manager/prj.ts b/src/manager/prj.ts index fc4184a..135c934 100644 --- a/src/manager/prj.ts +++ b/src/manager/prj.ts @@ -6,6 +6,7 @@ import { AbsPath, opeParam } from '../global'; import { PathSet } from '../global/util'; import { RawPrjInfo } from '../global/prjInfo'; import { hdlFile, hdlPath } from '../hdlFs'; +import { libManage } from './lib'; class PrjManage { // generate property template and write it to .vscode/property.json @@ -66,21 +67,30 @@ class PrjManage { } } + public getIgnoreFiles(): AbsPath[] { + return []; + } + public getPrjHardwareFiles(): AbsPath[] { const searchPathSet = new PathSet(); - const hardwareInfo = opeParam.prjInfo.arch.hardware; + const prjInfo = opeParam.prjInfo; + const hardwareInfo = prjInfo.arch.hardware; + + // handle library first + libManage.processLibFiles(prjInfo.library); + + // add possible folder to search + searchPathSet.checkAdd(hardwareInfo.src); + searchPathSet.checkAdd(hardwareInfo.sim); + searchPathSet.checkAdd(prjInfo.getLibraryCommonPaths()); + searchPathSet.checkAdd(prjInfo.getLibraryCustomPaths()); // TODO : make something like .gitignore + const ignores = this.getIgnoreFiles(); - // search src - searchPathSet.checkAdd(hardwareInfo.src); - - // search sim - searchPathSet.checkAdd(hardwareInfo.sim); - + // do search const searchPaths = searchPathSet.files; - - return hdlFile.getHDLFiles(searchPaths, []); + return hdlFile.getHDLFiles(searchPaths, ignores); } public initialise() { @@ -91,5 +101,6 @@ class PrjManage { const prjManage = new PrjManage(); export { - prjManage + prjManage, + PrjManage }; \ No newline at end of file