/* eslint-disable @typescript-eslint/naming-convention */ import assert = require('assert'); import * as chokidar from 'chokidar'; import * as vscode from 'vscode'; import * as fs from 'fs'; import { refreshArchTree } from '../function/treeView'; import { AbsPath, MainOutput, opeParam, PrjInfoDefaults, RelPath, ReportType } from '../global'; import { isSameSet } from '../global/util'; import { hdlFile, hdlPath } from '../hdlFs'; import { hdlParam, HdlSymbol } from '../hdlParser'; import { prjManage } from '../manager'; import { libManage } from '../manager/lib'; import type { HdlMonitor } from './index'; import { HdlLangID, ToolChainType } from '../global/enum'; import { hdlSymbolStorage } from '../function/lsp/core'; import { vlogLinterManager, vhdlLinterManager, svlogLinterManager } from '../function/lsp/linter'; enum Event { Add = 'add', // emit when add file AddDir = 'addDir', // emit when add folder Unlink = 'unlink', // emit when delete file UnlinkDir = 'unlinkDir', // emit when delete folder Change = 'change', // emit when file changed All = 'all', // all the change above Ready = 'ready', Raw = 'raw', Error = 'error' }; abstract class BaseAction { public listenChange(m: HdlMonitor) { const fSWatcher = this.selectFSWatcher(m); if (!fSWatcher) { MainOutput.report("FSWatcher hasn't been made!", ReportType.Error); return; } fSWatcher.on(Event.Change, path => this.change(path, m)); } public listenAdd(m: HdlMonitor) { const fSWatcher = this.selectFSWatcher(m); if (!fSWatcher) { MainOutput.report("FSWatcher hasn't been made!", ReportType.Error); return; } fSWatcher.on(Event.Add, path => this.add(path, m)); } public listenUnlink(m: HdlMonitor) { const fSWatcher = this.selectFSWatcher(m); if (!fSWatcher) { MainOutput.report("FSWatcher hasn't been made!", ReportType.Error); return; } fSWatcher.on(Event.Unlink, path => this.unlink(path, m)); } abstract selectFSWatcher(m: HdlMonitor): chokidar.FSWatcher | undefined; abstract change(path: AbsPath, m: HdlMonitor): Promise; abstract add(path: AbsPath, m: HdlMonitor): Promise; abstract unlink(path: AbsPath, m: HdlMonitor): Promise; } class HdlAction extends BaseAction { selectFSWatcher(m: HdlMonitor): chokidar.FSWatcher | undefined { return m.hdlMonitor; } async add(path: string, m: HdlMonitor): Promise { console.log('HdlAction add', path); path = hdlPath.toSlash(path); this.updateLinter(path); // check if it has been created if (hdlParam.hasHdlFile(path)) { MainOutput.report(' HdlFile ' + path + ' has been created', ReportType.Warn); return; } // create corresponding moduleFile await hdlParam.addHdlFile(path); refreshArchTree(); } async unlink(path: string, m: HdlMonitor): Promise { console.log('HdlAction unlink', path); // operation to process unlink of hdl files can be deleted in path = hdlPath.toSlash(path); hdlParam.deleteHdlFile(path); console.log(hdlParam); refreshArchTree(); const uri = vscode.Uri.file(path); const langID = hdlFile.getLanguageId(path); if (langID === HdlLangID.Verilog) { vlogLinterManager.remove(uri); } else if (langID === HdlLangID.Vhdl) { vhdlLinterManager.remove(uri); } else if (langID === HdlLangID.SystemVerilog) { svlogLinterManager.remove(uri); } } async unlinkDir(path: string, m: HdlMonitor): Promise { console.log('HdlAction unlinkDir', path); } async addDir(path: string, m: HdlMonitor): Promise { console.log('HdlAction addDir', path); } async change(path: string, m: HdlMonitor): Promise { console.log('HdlAction change'); path = hdlPath.toSlash(path); const langID = hdlFile.getLanguageId(path); // TODO : check performance if (langID === HdlLangID.Vhdl) { await this.updateSymbolStorage(path); await this.updateHdlParam(path); } await this.updateLinter(path); refreshArchTree(); } async updateSymbolStorage(path: string) { hdlSymbolStorage.updateSymbol(path); } async updateLinter(path: string) { const uri = vscode.Uri.file(path); const document = await vscode.workspace.openTextDocument(uri); const langID = hdlFile.getLanguageId(path); if (langID === HdlLangID.Verilog) { vlogLinterManager.lint(document); } else if (langID === HdlLangID.Vhdl) { vhdlLinterManager.lint(document); } else if (langID === HdlLangID.SystemVerilog) { svlogLinterManager.lint(document); } } async updateHdlParam(path: string) { const moduleFile = hdlParam.getHdlFile(path); if (!moduleFile) { return; } const fast = await HdlSymbol.fast(path); if (!fast) { // vscode.window.showErrorMessage('error happen when parse ' + path + '\nFail to update'); return; } // 1. update marco directly moduleFile.updateMacro(fast.macro); // 2. update modules one by one const uncheckedModuleNames = new Set(); for (const name of moduleFile.getAllModuleNames()) { uncheckedModuleNames.add(name); } for (const rawHdlModule of fast.content) { const moduleName = rawHdlModule.name; if (uncheckedModuleNames.has(moduleName)) { // match the same module, check then const originalModule = moduleFile.getHdlModule(moduleName); uncheckedModuleNames.delete(moduleName); originalModule?.update(rawHdlModule); } else { // no matched, create it const newModule = moduleFile.createHdlModule(rawHdlModule); newModule.makeNameToInstances(); newModule.solveUnhandleInstance(); } } // 3. delete module not visited yet for (const moduleName of uncheckedModuleNames) { moduleFile.deleteHdlModule(moduleName); } } } class PpyAction extends BaseAction { selectFSWatcher(m: HdlMonitor): chokidar.FSWatcher | undefined { return m.ppyMonitor; } async add(path: string, m: HdlMonitor): Promise { console.log('PpyAction add'); assert.equal(hdlPath.toSlash(path), opeParam.propertyJsonPath); await this.updateProperty(Event.Add, m); } async unlink(path: string, m: HdlMonitor): Promise { console.log('PpyAction unlink'); assert.equal(hdlPath.toSlash(path), opeParam.propertyJsonPath); await this.updateProperty(Event.Unlink, m); } async change(path: string, m: HdlMonitor): Promise { console.log('PpyAction change'); assert.equal(hdlPath.toSlash(path), opeParam.propertyJsonPath); await this.updateProperty(Event.Change, m); console.log(hdlParam); } // get path set from opeParam that used to tell if need to remake HdlMonitor private getImportantPathSet(): Set { const pathSet = new Set(); pathSet.add(opeParam.prjInfo.hardwareSimPath); pathSet.add(opeParam.prjInfo.hardwareSrcPath); for (const path of opeParam.prjInfo.getLibraryCommonPaths()) { pathSet.add(path); } for (const path of opeParam.prjInfo.getLibraryCustomPaths()) { pathSet.add(path); } return pathSet; } public async updateProperty(e: Event, m: HdlMonitor) { const originalPathSet = this.getImportantPathSet(); const originalHdlFiles = await prjManage.getPrjHardwareFiles(); const originalLibState = opeParam.prjInfo.library.state; const rawPrjInfo = opeParam.getRawUserPrjInfo(); // when delete, make ws path to be main parse path if (e === Event.Unlink) { console.log('unlink ppy, PrjInfoDefaults.arch:', PrjInfoDefaults.arch); rawPrjInfo.arch = PrjInfoDefaults.arch; } opeParam.mergePrjInfo(rawPrjInfo); await prjManage.refreshPrjFolder(); const currentPathSet = this.getImportantPathSet(); const currentLibState = opeParam.prjInfo.library.state; if (isSameSet(originalPathSet, currentPathSet)) { // skip hdl remake if (originalLibState !== currentLibState) { const fileChange = await libManage.processLibFiles(opeParam.prjInfo.library); MainOutput.report(`libManage finish process, add ${fileChange.add.length} files, del ${fileChange.del.length} files`, ReportType.Info); } } else { // update hdl monitor await this.refreshHdlMonitor(m, originalHdlFiles); } refreshArchTree(); } public diffNewOld(newFiles: AbsPath[], oldFiles: AbsPath[]) { const uncheckHdlFileSet = new Set(oldFiles); const addFiles: AbsPath[] = []; const delFiles: AbsPath[] = []; for (const path of newFiles) { if (!uncheckHdlFileSet.has(path)) { addFiles.push(path); } else { uncheckHdlFileSet.delete(path); } } for (const path of uncheckHdlFileSet) { hdlParam.deleteHdlFile(path); delFiles.push(path); } return { addFiles, delFiles }; } public async refreshHdlMonitor(m: HdlMonitor, originalHdlFiles: AbsPath[]) { m.remakeHdlMonitor(); const newFiles = await prjManage.getPrjHardwareFiles(); const { addFiles, delFiles } = this.diffNewOld(newFiles, originalHdlFiles); const options: vscode.ProgressOptions = { location: vscode.ProgressLocation.Notification }; options.title = 'update HdlParam'; await vscode.window.withProgress(options, async () => await this.updateHdlParam(addFiles, delFiles)); if (opeParam.prjInfo.toolChain === ToolChainType.Xilinx) { options.title = 'update PL'; await vscode.window.withProgress(options, async () => await this.updatePL(addFiles, delFiles)); } } public async updateHdlParam(addFiles: AbsPath[], delFiles: AbsPath[]) { for (const path of addFiles) { await hdlParam.addHdlFile(path); } for (const path of delFiles) { hdlParam.deleteHdlFile(path); } } public async updatePL(addFiles: AbsPath[], delFiles: AbsPath[]) { // current only support xilinx if (prjManage.pl) { await prjManage.pl.addFiles(addFiles); await prjManage.pl.delFiles(delFiles); } else { MainOutput.report('PL is not registered', ReportType.Warn); } } } const hdlAction = new HdlAction(); const ppyAction = new PpyAction(); export { hdlAction, ppyAction };