import * as vscode from 'vscode'; import * as fs from 'fs'; import * as util from '../util'; import { hdlFile, hdlPath } from '../../../hdlFs'; import { hdlParam } from '../../../hdlParser'; import { AbsPath, MainOutput, RelPath, ReportType } from '../../../global'; import { Define, Include, RawSymbol } from '../../../hdlParser/common'; import { HdlInstance, HdlModule } from '../../../hdlParser/core'; import { vlogKeyword } from '../util/keyword'; import { instanceVlogCode } from '../../sim/instance'; // class VlogIncludeCompletionProvider implements vscode.CompletionItemProvider { // public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): vscode.ProviderResult> { // // console.log('VlogIncludeCompletionProvider'); // try { // const items = this.provideIncludeFiles(document, position); // return items; // } catch (err) { // console.log(err); // } // } // private provideIncludeFiles(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] { // if (position.character === 0) { // return []; // } // const filePath = hdlPath.toSlash(document.fileName); // const lineText = document.lineAt(position).text; // let firstQIndex = lineText.lastIndexOf('"', position.character - 1); // let lastQIndex = lineText.indexOf('"', position.character); // if (firstQIndex !== -1 && lastQIndex !== -1) { // const currentPath = lineText.substring(firstQIndex + 1, lastQIndex); // const folderName = currentPath.length === 0 ? '.' : currentPath; // const folderAbsPath = hdlPath.rel2abs(filePath, folderName); // return this.filterIncludeFiles(folderAbsPath, filePath); // } // return []; // } // private filterIncludeFiles(folderPath: AbsPath, currentPath: AbsPath) { // if (fs.existsSync(folderPath)) { // const suggestFiles = []; // for (const fileName of fs.readdirSync(folderPath)) { // const filePath = hdlPath.join(folderPath, fileName); // if (filePath === currentPath) { // continue; // } // const stat = fs.statSync(filePath); // const clItem = new vscode.CompletionItem(fileName); // if (stat.isDirectory()) { // clItem.kind = vscode.CompletionItemKind.Folder; // } else if (stat.isFile()) { // clItem.kind = vscode.CompletionItemKind.File; // } // suggestFiles.push(clItem); // } // return suggestFiles; // } // return []; // } // }; // class VlogMacroCompletionProvider implements vscode.CompletionItemProvider { // public async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Promise | null | undefined> { // // console.log('VlogMacroCompletionProvider'); // try { // const targetWordRange = document.getWordRangeAtPosition(position, /[`_0-9a-zA-Z]+/); // const targetWord = document.getText(targetWordRange); // const filePath = document.fileName; // const symbolResult = await hdlSymbolStorage.getSymbol(filePath); // if (!symbolResult) { // return null; // } // const items = this.provideMacros(targetWord, symbolResult.macro.defines); // return items; // } catch (err) { // console.log(err); // } // } // private provideMacros(targetWord: string, defines: Define[]): vscode.CompletionItem[] { // const suggestMacros: vscode.CompletionItem[] = []; // if (!defines || defines.length === 0) { // return suggestMacros; // } // for (const define of defines) { // const name = '`' + define.name; // const clItem = new vscode.CompletionItem(name, vscode.CompletionItemKind.Constant); // clItem.detail = 'macro ' + define.replacement; // clItem.insertText = targetWord.startsWith('`') ? define.name : name; // suggestMacros.push(clItem); // } // return suggestMacros; // } // } // class VlogPositionPortProvider implements vscode.CompletionItemProvider { // public async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Promise | null | undefined> { // // console.log('enter VlogPositionPortProvider'); // try { // const suggestPositionPorts: vscode.CompletionItem[] = []; // const filePath = hdlPath.toSlash(document.fileName); // const symbolResult = await hdlSymbolStorage.getSymbol(filePath); // // console.log(symbolResult?.content); // // console.log(position.character, position.line); // if (!symbolResult) { // return null; // } // const scopeSymbols = util.locateVlogSymbol(position, symbolResult.content); // if (!scopeSymbols || // !scopeSymbols.module || // !scopeSymbols.symbols || // !hdlParam.hasHdlModule(filePath, scopeSymbols.module.name)) { // return suggestPositionPorts; // } // const currentModule = hdlParam.getHdlModule(filePath, scopeSymbols.module.name); // if (!currentModule) { // return; // } // const currentInst = util.filterInstanceByPosition(position, scopeSymbols.symbols, currentModule); // // find instance and instMod is not null (solve the dependence already) // if (currentInst && currentInst.module && currentInst.instModPath) { // const portsparams = this.providePositionPorts(position, currentInst); // suggestPositionPorts.push(...portsparams); // } // return suggestPositionPorts; // } catch (err) { // console.log(err); // } // } // private providePositionPorts(position: vscode.Position, currentInst: HdlInstance): vscode.CompletionItem[] { // if (!currentInst.module) { // return []; // } // const params = currentInst.instparams; // const ports = currentInst.instports; // if (params && // util.positionAfterEqual(position, params.start) && // util.positionAfterEqual(params.end, position)) { // return currentInst.module.params.map(param => { // const clItem = new vscode.CompletionItem(param.name, vscode.CompletionItemKind.Constant); // clItem.detail = 'param'; // return clItem; // }); // } // if (ports && // util.positionAfterEqual(position, ports.start) && // util.positionAfterEqual(ports.end, position)) { // return currentInst.module.ports.map(port => { // const clItem = new vscode.CompletionItem(port.name, vscode.CompletionItemKind.Interface); // clItem.detail = 'port'; // return clItem; // }); // } // return []; // } // } // class VlogCompletionProvider implements vscode.CompletionItemProvider { // public async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Promise | null | undefined> { // // console.log('VlogCompletionProvider'); // try { // const filePath = hdlPath.toSlash(document.fileName); // // 1. provide keyword // const completions = this.makeKeywordItems(document, position); // completions.push(...this.makeCompilerKeywordItems(document, position)); // completions.push(...this.makeSystemKeywordItems(document, position)); // const symbolResult = await hdlSymbolStorage.getSymbol(filePath); // if (!symbolResult) { // return completions; // } // // locate at one module // const scopeSymbols = util.locateVlogSymbol(position, symbolResult.content); // if (!scopeSymbols || // !scopeSymbols.module || // !hdlParam.hasHdlModule(filePath, scopeSymbols.module.name)) { // // MainOutput.report('Fail to get HdlModule ' + filePath + ' ' + scopeSymbols?.module.name, ReportType.Debug); // return completions; // } // // find wrapper module // const currentModule = hdlParam.getHdlModule(filePath, scopeSymbols.module.name); // if (!currentModule) { // return completions; // } // // 3. provide modules // const suggestModulesPromise = this.provideModules(document, position, filePath, symbolResult.macro.includes); // // 4. provide params and ports of wrapper module // const suggestParamsPortsPromise = this.provideParamsPorts(currentModule); // // 5. provide nets // const suggestNetsPromise = this.provideNets(scopeSymbols.symbols); // // collect // completions.push(...await suggestModulesPromise); // completions.push(...await suggestParamsPortsPromise); // completions.push(...await suggestNetsPromise); // return completions; // } catch (err) { // console.log(err); // } // } // private makeKeywordItems(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] { // const vlogKeywordItems: vscode.CompletionItem[] = []; // for (const keyword of vlogKeyword.keys()) { // const clItem = this.makekeywordCompletionItem(keyword); // vlogKeywordItems.push(clItem); // } // return vlogKeywordItems; // } // private makeCompilerKeywordItems(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] { // const items = []; // const targetRange = document.getWordRangeAtPosition(position, /[`_0-9a-zA-Z]+/); // const targetWord = document.getText(targetRange); // const prefix = targetWord.startsWith('`') ? '' : '`'; // for (const keyword of vlogKeyword.compilerKeys()) { // const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword); // clItem.insertText = new vscode.SnippetString(prefix + keyword); // clItem.detail = 'compiler directive'; // items.push(clItem); // } // return items; // } // private makeSystemKeywordItems(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] { // const items = []; // for (const keyword of vlogKeyword.systemKeys()) { // const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Method); // clItem.insertText = new vscode.SnippetString('\\$' + keyword + '($1);'); // clItem.detail = 'system task'; // items.push(clItem); // } // return items; // } // private makekeywordCompletionItem(keyword: string): vscode.CompletionItem { // const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword); // clItem.detail = 'verilog keyword'; // switch (keyword) { // case 'begin': clItem.insertText = new vscode.SnippetString("begin$1\nend"); break; // case 'function': clItem.insertText = new vscode.SnippetString("function ${1:name}\n\nendfunction"); break; // default: break; // } // return clItem; // } // private async provideModules(document: vscode.TextDocument, position: vscode.Position, filePath: AbsPath, includes: Include[]): Promise { // const suggestModules: vscode.CompletionItem[] = []; // const lspVlogConfig = vscode.workspace.getConfiguration('digital-ide.function.lsp.completion.vlog'); // const auto-add-include: boolean = lspVlogConfig.get('auto-add-include', true); // const auto-add-output-declaration: boolean = lspVlogConfig.get('auto-add-output-declaration', true); // const includePaths = new Set(); // let lastIncludeLine = 0; // for (const include of includes) { // const absIncludePath = hdlPath.rel2abs(filePath, include.path); // includePaths.add(absIncludePath); // lastIncludeLine = Math.max(include.range.end.line, lastIncludeLine); // } // const insertPosition = new vscode.Position(lastIncludeLine, 0); // const insertRange = new vscode.Range(insertPosition, insertPosition); // const fileFolder = hdlPath.resolve(filePath, '..'); // // used only when auto-add-output-declaration is true // let completePrefix = ''; // if (auto-add-output-declaration) { // const wordRange = document.getWordRangeAtPosition(position); // const countStart = wordRange ? wordRange.start.character : position.character; // const spaceNumber = Math.floor(countStart / 4) * 4; // console.log(wordRange, countStart, spaceNumber); // completePrefix = ' '.repeat(spaceNumber); // } // for (const module of hdlParam.getAllHdlModules()) { // const clItem = new vscode.CompletionItem(module.name, vscode.CompletionItemKind.Class); // // feature 1 : auto add include path if there's no corresponding include path // if (auto-add-include && !includePaths.has(module.path)) { // const relPath: RelPath = hdlPath.relative(fileFolder, module.path); // const includeString = '`include "' + relPath + '"\n'; // const textEdit = new vscode.TextEdit(insertRange, includeString); // clItem.additionalTextEdits = [textEdit]; // } // // feature 2 : auto complete instance // if (auto-add-output-declaration) { // const snippetString = instanceVlogCode(module, '', true); // clItem.insertText = new vscode.SnippetString(snippetString); // } // clItem.detail = 'module'; // suggestModules.push(clItem); // } // return suggestModules; // } // private async provideParamsPorts(module: HdlModule): Promise { // if (!module) { // return []; // } // const suggestParamsPorts = []; // for (const param of module.params) { // const clItem = new vscode.CompletionItem(param.name, vscode.CompletionItemKind.Constant); // clItem.detail = 'param'; // suggestParamsPorts.push(clItem); // } // for (const port of module.ports) { // const clItem = new vscode.CompletionItem(port.name, vscode.CompletionItemKind.Interface); // clItem.detail = 'port'; // suggestParamsPorts.push(clItem); // } // return suggestParamsPorts; // } // private async provideNets(symbols: RawSymbol[]): Promise { // if (!symbols) { // return []; // } // const suggestNets = []; // for (const symbol of symbols) { // if (symbol.type === 'wire' || symbol.type === 'reg') { // const clItem = new vscode.CompletionItem(symbol.name, vscode.CompletionItemKind.Variable); // clItem.sortText = ''; // clItem.detail = symbol.type; // suggestNets.push(clItem); // } // } // return suggestNets; // } // }; // const vlogCompletionProvider = new VlogCompletionProvider(); // const vlogIncludeCompletionProvider = new VlogIncludeCompletionProvider(); // const vlogMacroCompletionProvider = new VlogMacroCompletionProvider(); // const vlogPositionPortProvider = new VlogPositionPortProvider(); // export { // vlogCompletionProvider, // vlogIncludeCompletionProvider, // vlogMacroCompletionProvider, // vlogPositionPortProvider // };