From 6ce85c4dc25bd2c11e7b92c262e6786998282834 Mon Sep 17 00:00:00 2001 From: Andrew Nolte Date: Tue, 12 Mar 2024 20:11:47 -0400 Subject: [PATCH] support module/interface definitions and hovers by searching for file name --- src/commands/ModuleInstantiation.ts | 140 +++++++++++------------- src/ctags.ts | 119 ++++++++++++++------ src/extension.ts | 17 +-- src/providers/CompletionItemProvider.ts | 8 +- src/providers/DefinitionProvider.ts | 28 ++--- src/providers/DocumentSymbolProvider.ts | 7 +- src/providers/HoverProvider.ts | 29 ++--- 7 files changed, 195 insertions(+), 153 deletions(-) diff --git a/src/commands/ModuleInstantiation.ts b/src/commands/ModuleInstantiation.ts index 19396bbb..03be5490 100644 --- a/src/commands/ModuleInstantiation.ts +++ b/src/commands/ModuleInstantiation.ts @@ -14,71 +14,63 @@ export function instantiateModuleInteract() { }); } -function instantiateModule(srcpath: string): Thenable { - return new Promise((resolve, _reject) => { +async function instantiateModule(srcpath: string): Promise { // Using Ctags to get all the modules in the file let moduleName: string = ''; let portsName: string[] = []; let parametersName: string[] = []; - let ctags: ModuleTags = new ModuleTags(logger); + let file: vscode.TextDocument = vscode.window.activeTextEditor.document; + let ctags: ModuleTags = new ModuleTags(logger, file); logger.info('Executing ctags for module instantiation'); - ctags - .execCtags(srcpath) - .then((output) => { - ctags.buildSymbolsList(output); - }) - .then(async () => { - let module: Symbol; - let modules: Symbol[] = ctags.symbols.filter((tag) => tag.type === 'module'); - // No modules found - if (modules.length <= 0) { - vscode.window.showErrorMessage('Verilog-HDL/SystemVerilog: No modules found in the file'); - return; - } - // Only one module found - else if (modules.length === 1) { - module = modules[0]; - } - // many modules found - else if (modules.length > 1) { - moduleName = await vscode.window.showQuickPick( - ctags.symbols.filter((tag) => tag.type === 'module').map((tag) => tag.name), - { - placeHolder: 'Choose a module to instantiate', - } - ); - if (moduleName === undefined) { - return; - } - module = modules.filter((tag) => tag.name === moduleName)[0]; - } - let scope = module.parentScope != '' ? module.parentScope + '.' + module.name : module.name; - let ports: Symbol[] = ctags.symbols.filter( - (tag) => tag.type === 'port' && tag.parentType === 'module' && tag.parentScope === scope - ); - portsName = ports.map((tag) => tag.name); - let params: Symbol[] = ctags.symbols.filter( - (tag) => - tag.type === 'parameter' && tag.parentType === 'module' && tag.parentScope === scope - ); - parametersName = params.map((tag) => tag.name); - logger.info('Module name: ' + module.name); - let paramString = ``; - if (parametersName.length > 0) { - paramString = `\n#(\n${instantiatePort(parametersName)})\n`; + let output = await ctags.execCtags(srcpath); + await ctags.buildSymbolsList(output); + let module: Symbol; + let modules: Symbol[] = ctags.symbols.filter((tag) => tag.type === 'module'); + // No modules found + if (modules.length <= 0) { + vscode.window.showErrorMessage('Verilog-HDL/SystemVerilog: No modules found in the file'); + return undefined; + } + // Only one module found + else if (modules.length === 1) { + module = modules[0]; + } + // many modules found + else if (modules.length > 1) { + moduleName = await vscode.window.showQuickPick( + ctags.symbols.filter((tag) => tag.type === 'module').map((tag) => tag.name), + { + placeHolder: 'Choose a module to instantiate', } - logger.info('portsName: ' + portsName.toString()); - resolve( - new vscode.SnippetString() - .appendText(module.name + ' ') - .appendText(paramString) - .appendPlaceholder('u_') - .appendPlaceholder(`${module.name}(\n`) - .appendText(instantiatePort(portsName)) - .appendText(');\n') - ); - }); - }); + ); + if (moduleName === undefined) { + return undefined; + } + module = modules.filter((tag) => tag.name === moduleName)[0]; + } + let scope = module.parentScope != '' ? module.parentScope + '.' + module.name : module.name; + let ports: Symbol[] = ctags.symbols.filter( + (tag) => tag.type === 'port' && tag.parentType === 'module' && tag.parentScope === scope + ); + portsName = ports.map((tag) => tag.name); + let params: Symbol[] = ctags.symbols.filter( + (tag) => + tag.type === 'parameter' && tag.parentType === 'module' && tag.parentScope === scope + ); + parametersName = params.map((tag) => tag.name); + logger.info('Module name: ' + module.name); + let paramString = ``; + if (parametersName.length > 0) { + paramString = `\n#(\n${instantiatePort(parametersName)})\n`; + } + logger.info('portsName: ' + portsName.toString()); + return new vscode.SnippetString() + .appendText(module.name + ' ') + .appendText(paramString) + .appendPlaceholder('u_') + .appendPlaceholder(`${module.name}(\n`) + .appendText(instantiatePort(portsName)) + .appendText(');\n'); } function instantiatePort(ports: string[]): string { @@ -103,7 +95,7 @@ function instantiatePort(ports: string[]): string { return port; } -function selectFile(currentDir?: string): Thenable { +async function selectFile(currentDir?: string): Promise { currentDir = currentDir || vscode.workspace.rootPath; let dirs = getDirectories(currentDir); @@ -129,24 +121,22 @@ function selectFile(currentDir?: string): Thenable { }); }); - return vscode.window + let selected = await vscode.window .showQuickPick(items, { placeHolder: 'Choose the module file', - }) - .then((selected) => { - if (!selected) { - return undefined; - } + }); + if (!selected) { + return undefined; + } - // if is a directory - let location = path.join(currentDir, selected.label); - if (fs.statSync(location).isDirectory()) { - return selectFile(location); - } + // if is a directory + let location = path.join(currentDir, selected.label); + if (fs.statSync(location).isDirectory()) { + return selectFile(location); + } - // return file path - return location; - }); + // return file path + return location; } function getDirectories(srcpath: string): string[] { @@ -160,7 +150,7 @@ function getFiles(srcpath: string): string[] { } class ModuleTags extends Ctags { - buildSymbolsList(tags: string): Thenable { + buildSymbolsList(tags: string): Promise { if (tags === '') { return undefined; } diff --git a/src/ctags.ts b/src/ctags.ts index 27c1f6ed..8acf4d35 100644 --- a/src/ctags.ts +++ b/src/ctags.ts @@ -141,16 +141,13 @@ export class Ctags { isDirty: boolean; private logger: Logger; - constructor(logger: Logger) { + constructor(logger: Logger, document: vscode.TextDocument) { this.symbols = []; this.isDirty = true; this.logger = logger; + this.doc = document; } - setDocument(doc: vscode.TextDocument) { - this.doc = doc; - this.clearSymbols(); - } clearSymbols() { this.isDirty = true; @@ -161,7 +158,7 @@ export class Ctags { return this.symbols; } - execCtags(filepath: string): Thenable { + async execCtags(filepath: string): Promise { this.logger.info('executing ctags'); let binPath: string = ( @@ -210,13 +207,13 @@ export class Ctags { return undefined; } - buildSymbolsList(tags: string): Thenable { + async buildSymbolsList(tags: string): Promise { try { if (this.isDirty) { this.logger.info('building symbols'); if (tags === '') { this.logger.error('No output from ctags'); - return undefined; + return; } // Parse ctags output let lines: string[] = tags.split(/\r?\n/); @@ -261,54 +258,112 @@ export class Ctags { this.logger.info('Symbols: ' + this.symbols.toString()); this.isDirty = false; } - return Promise.resolve(); } catch (e) { this.logger.error(e.toString()); } - return undefined; } - index(): Thenable { - this.logger.info('indexing...'); - return new Promise((resolve, _reject) => { - this.execCtags(this.doc.uri.fsPath) - .then((output) => this.buildSymbolsList(output)) - .then(() => resolve()); - }); + async index(): Promise { + this.logger.info('indexing ', this.doc.uri.fsPath); + + let output = await this.execCtags(this.doc.uri.fsPath) + console.log("output", output) + await this.buildSymbolsList(output); } } export class CtagsManager { - private static ctags: Ctags; + private filemap: Map = new Map(); private logger: Logger; - constructor(logger: Logger) { + configure(logger: Logger) { this.logger = logger; - CtagsManager.ctags = new Ctags(logger.getChild('Ctags')); - } - - configure() { this.logger.info('ctags manager configure'); vscode.workspace.onDidSaveTextDocument(this.onSave.bind(this)); + vscode.workspace.onDidCloseTextDocument(this.onClose.bind(this)); + } + + getCtags(doc: vscode.TextDocument): Ctags { + let ctags: Ctags = this.filemap.get(doc); + if (ctags === undefined) { + ctags = new Ctags(this.logger, doc); + this.filemap.set(doc, ctags); + } + return ctags; + } + onClose(doc: vscode.TextDocument) { + this.logger.info('on close'); + this.filemap.delete(doc); } onSave(doc: vscode.TextDocument) { this.logger.info('on save'); - let ctags: Ctags = CtagsManager.ctags; - if (ctags.doc === undefined || ctags.doc.uri.fsPath === doc.uri.fsPath) { - CtagsManager.ctags.clearSymbols(); - } + let ctags: Ctags = this.getCtags(doc); + ctags.clearSymbols(); } - static async getSymbols(doc: vscode.TextDocument): Promise { - let ctags: Ctags = CtagsManager.ctags; - if (ctags.doc === undefined || ctags.doc.uri.fsPath !== doc.uri.fsPath) { - ctags.setDocument(doc); - } + async getSymbols(doc: vscode.TextDocument): Promise { + let ctags: Ctags = this.getCtags(doc); // If dirty, re index and then build symbols if (ctags.isDirty) { + console.log("indxing "); await ctags.index(); } return ctags.symbols; } + + async findMatches(document: vscode.TextDocument, targetText: string): Promise { + console.log("finding matches asdf", document.uri.fsPath, "for", targetText); + let symbols: Symbol[] = await this.getSymbols(document); + console.log("symbols", symbols, "for", document.uri.fsPath, targetText) + let matchingSymbols: Symbol[] = []; + for (let i of symbols) { + if (i.name === targetText) { + matchingSymbols.push(i); + } + } + // console.log("matchingSymbols", matchingSymbols, "for", document.uri.fsPath, targetText); + let ret: vscode.DefinitionLink[] = matchingSymbols.map((i) => { + return { + document: document, + targetUri: document.uri, + targetRange: new vscode.Range( + i.startPosition, + new vscode.Position(i.startPosition.line, Number.MAX_VALUE) + ), + targetSelectionRange: new vscode.Range(i.startPosition, i.endPosition), + }; + }); + console.log("ret", ret); + return ret; + } + + /// also searched in targetText.sv + async findSymbol(document: vscode.TextDocument, targetText: string): Promise { + // we know we want to search the current doc + // let tasks = [this.findMatches(document, targetText)]; + + let matches = await this.findMatches(document, targetText); + + + // kick off async job that looks for module.sv + + + let searchPattern = new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], `**/${targetText}.sv`); + let files = await vscode.workspace.findFiles(searchPattern); + if (files.length !== 0) { + console.log("found", files[0].fsPath); + let file = await vscode.workspace.openTextDocument(files[0]); + // tasks.push(this.findMatches(file, targetText)); + let modMatches = await this.findMatches(file, targetText); + matches = matches.concat(modMatches); + } + + // const lists = await Promise.all(tasks); + // const lists = matches.concat(modMatches); + // combine into one using reduce + // let matchingSymbols = lists.reduce((acc, val) => acc.concat(val), []); + console.log("matchingSymbols", matches, "for", document.uri.fsPath, targetText); + return matches; + } } diff --git a/src/extension.ts b/src/extension.ts index 118d0669..abb95fa4 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -15,7 +15,7 @@ import { ExtensionManager } from './extensionManager'; import { createLogger, Logger } from './logger'; export var logger: Logger; // Global logger -var ctagsManager: CtagsManager; +var ctagsManager = new CtagsManager(); let extensionID: string = 'mshr-h.veriloghdl'; let lintManager: LintManager; @@ -36,12 +36,12 @@ export function activate(context: vscode.ExtensionContext) { }); // Configure ctags - ctagsManager = new CtagsManager(logger.getChild('CtagsManager')); - ctagsManager.configure(); + ctagsManager.configure(logger); // Configure Document Symbol Provider let verilogDocumentSymbolProvider = new DocumentSymbolProvider.VerilogDocumentSymbolProvider( - logger.getChild('VerilogDocumentSymbolProvider') + logger.getChild('VerilogDocumentSymbolProvider'), + ctagsManager, ); context.subscriptions.push( vscode.languages.registerDocumentSymbolProvider( @@ -68,7 +68,8 @@ export function activate(context: vscode.ExtensionContext) { // Configure Completion Item Provider // Trigger on ".", "(", "=" let verilogCompletionItemProvider = new CompletionItemProvider.VerilogCompletionItemProvider( - logger.getChild('VerilogCompletionItemProvider') + logger.getChild('VerilogCompletionItemProvider'), + ctagsManager, ); context.subscriptions.push( vscode.languages.registerCompletionItemProvider( @@ -103,7 +104,8 @@ export function activate(context: vscode.ExtensionContext) { // Configure Hover Providers let verilogHoverProvider = new HoverProvider.VerilogHoverProvider( - logger.getChild('VerilogHoverProvider') + logger.getChild('VerilogHoverProvider'), + ctagsManager, ); context.subscriptions.push( vscode.languages.registerHoverProvider( @@ -124,7 +126,8 @@ export function activate(context: vscode.ExtensionContext) { // Configure Definition Providers let verilogDefinitionProvider = new DefinitionProvider.VerilogDefinitionProvider( - logger.getChild('VerilogDefinitionProvider') + logger.getChild('VerilogDefinitionProvider'), + ctagsManager ); context.subscriptions.push( vscode.languages.registerDefinitionProvider( diff --git a/src/providers/CompletionItemProvider.ts b/src/providers/CompletionItemProvider.ts index e74ab328..bf22627d 100644 --- a/src/providers/CompletionItemProvider.ts +++ b/src/providers/CompletionItemProvider.ts @@ -6,9 +6,11 @@ import { Logger } from '../logger'; export class VerilogCompletionItemProvider implements vscode.CompletionItemProvider { private logger: Logger; - - constructor(logger: Logger) { + private ctagsManager: CtagsManager; + constructor(logger: Logger, + ctagsManager: CtagsManager){ this.logger = logger; + this.ctagsManager = ctagsManager; } //TODO: Better context based completion items @@ -21,7 +23,7 @@ export class VerilogCompletionItemProvider implements vscode.CompletionItemProvi this.logger.info('Completion items requested'); let items: vscode.CompletionItem[] = []; - let symbols: Symbol[] = await CtagsManager.getSymbols(document); + let symbols: Symbol[] = await this.ctagsManager.getSymbols(document); symbols.forEach((symbol) => { let newItem: vscode.CompletionItem = new vscode.CompletionItem( symbol.name, diff --git a/src/providers/DefinitionProvider.ts b/src/providers/DefinitionProvider.ts index 599a635f..d0a2e275 100644 --- a/src/providers/DefinitionProvider.ts +++ b/src/providers/DefinitionProvider.ts @@ -1,13 +1,18 @@ // SPDX-License-Identifier: MIT import * as vscode from 'vscode'; import { BsvInfoProviderManger } from '../BsvProvider'; -import { CtagsManager, Symbol } from '../ctags'; +import { CtagsManager } from '../ctags'; import { Logger } from '../logger'; + + export class VerilogDefinitionProvider implements vscode.DefinitionProvider { private logger: Logger; - constructor(logger: Logger) { + private ctagsManager: CtagsManager; + constructor(logger: Logger, + ctagsManager: CtagsManager){ this.logger = logger; + this.ctagsManager = ctagsManager; } async provideDefinition( @@ -23,25 +28,8 @@ export class VerilogDefinitionProvider implements vscode.DefinitionProvider { } // hover word let targetText = document.getText(textRange); - let symbols: Symbol[] = await CtagsManager.getSymbols(document); - let matchingSymbols: Symbol[] = []; - let definitions: vscode.DefinitionLink[] = []; // find all matching symbols - for (let i of symbols) { - if (i.name === targetText) { - matchingSymbols.push(i); - } - } - for (let i of matchingSymbols) { - definitions.push({ - targetUri: document.uri, - targetRange: new vscode.Range( - i.startPosition, - new vscode.Position(i.startPosition.line, Number.MAX_VALUE) - ), - targetSelectionRange: new vscode.Range(i.startPosition, i.endPosition), - }); - } + let definitions: vscode.DefinitionLink[] = await this.ctagsManager.findSymbol(document, targetText); this.logger.info(definitions.length + ' definitions returned'); return definitions; } diff --git a/src/providers/DocumentSymbolProvider.ts b/src/providers/DocumentSymbolProvider.ts index 6b5002aa..9f1995c5 100644 --- a/src/providers/DocumentSymbolProvider.ts +++ b/src/providers/DocumentSymbolProvider.ts @@ -8,8 +8,11 @@ export class VerilogDocumentSymbolProvider implements vscode.DocumentSymbolProvi public docSymbols: vscode.DocumentSymbol[] = []; private logger: Logger; - constructor(logger: Logger) { + private ctagsManager: CtagsManager; + constructor(logger: Logger, + ctagsManager: CtagsManager){ this.logger = logger; + this.ctagsManager = ctagsManager; } async provideDocumentSymbols( @@ -17,7 +20,7 @@ export class VerilogDocumentSymbolProvider implements vscode.DocumentSymbolProvi _token: vscode.CancellationToken ): Promise { this.logger.info('[VerilogSymbol] Symbols Requested: ' + document.uri); - let symbols: Symbol[] = await CtagsManager.getSymbols(document); + let symbols: Symbol[] = await this.ctagsManager.getSymbols(document); this.logger.info('[VerilogSymbol] Symbols: ' + symbols.toString()); this.docSymbols = this.buildDocumentSymbolList(symbols); this.logger.info(this.docSymbols.length + ' top-level symbols returned'); diff --git a/src/providers/HoverProvider.ts b/src/providers/HoverProvider.ts index 576d33fe..a1712470 100644 --- a/src/providers/HoverProvider.ts +++ b/src/providers/HoverProvider.ts @@ -8,9 +8,11 @@ import { Logger } from '../logger'; export class VerilogHoverProvider implements vscode.HoverProvider { // lang: verilog / systemverilog private logger: Logger; - - constructor(logger: Logger) { + private ctagsManager: CtagsManager; + constructor(logger: Logger, + ctagsManager: CtagsManager){ this.logger = logger; + this.ctagsManager = ctagsManager; } public async provideHover( @@ -26,22 +28,21 @@ export class VerilogHoverProvider implements vscode.HoverProvider { } // hover word let targetText = document.getText(textRange); - let symbols: Symbol[] = await CtagsManager.getSymbols(document); + let matches: vscode.DefinitionLink[] = await this.ctagsManager.findSymbol(document, targetText); // find symbol - for (let i of symbols) { + for (let i of matches) { // returns the first found tag. Disregards others // TODO: very basic hover implementation. Can be extended - if (i.name === targetText) { - let codeRange = new vscode.Range( - i.startPosition, - new vscode.Position(i.startPosition.line, Number.MAX_VALUE) - ); - let code = document.getText(codeRange).trim(); - let hoverText: vscode.MarkdownString = new vscode.MarkdownString(); - hoverText.appendCodeblock(code, document.languageId); - this.logger.info('Hover object returned'); - return new vscode.Hover(hoverText); + let doc = document; + if (i.targetUri !== document.uri) { + doc = await vscode.workspace.openTextDocument(i.targetUri); } + // make a range 5 more lines + let code = doc.getText(i.targetRange).trim(); + let hoverText: vscode.MarkdownString = new vscode.MarkdownString(); + hoverText.appendCodeblock(code, document.languageId); + this.logger.info('Hover object returned'); + return new vscode.Hover(hoverText); } this.logger.warn('Hover object not found'); return undefined;