diff --git a/.travis.yml b/.travis.yml index 077db43..a18965b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,17 +2,21 @@ sudo: false os: - linux - + +language: node_js +node_js: + - "7" + before_install: - if [ $TRAVIS_OS_NAME == "linux" ]; then export CXX="g++-4.9" CC="gcc-4.9" DISPLAY=:99.0; sh -e /etc/init.d/xvfb start; sleep 3; fi - + install: - npm install - npm run vscode:prepublish - + script: - npm test --silent diff --git a/package.json b/package.json index fa78d5a..da46fb4 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "url": "https://github.com/mshr-h/vscode-systemverilog-support.git" }, "engines": { - "vscode": "^0.10.1" + "vscode": "^1.10.0" }, "categories": [ "Languages" @@ -28,6 +28,10 @@ "snippets": [{ "language": "systemverilog", "path": "./snippets/snippets.json" + }], + "commands": [{ + "command": "extension.systemverilog.instantiateModule", + "title": "System Verilog: Instantiate Module" }] }, "activationEvents": [ @@ -41,6 +45,7 @@ }, "devDependencies": { "typescript": "^2.0.3", - "vscode": "^1.0.0" + "vscode": "^1.0.0", + "@types/node": "*" } } diff --git a/src/extension.ts b/src/extension.ts index 31a07e2..570875b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,52 +1,57 @@ // The module 'vscode' contains the VS Code extensibility API // Import the necessary extensibility types to use in your code below -import {ExtensionContext, Position, TextDocument, CancellationToken, languages, Hover, HoverProvider} from 'vscode'; +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; // This method is called when your extension is activated. Activation is // controlled by the activation events defined in package.json. -export function activate(context: ExtensionContext) { +export function activate(context: vscode.ExtensionContext) { // System Verilog Hover Provider context.subscriptions.push( - languages.registerHoverProvider('systemverilog', + vscode.languages.registerHoverProvider('systemverilog', new SystemVerilogHoverProvider() ) ); + + // instantiate system verilog module + context.subscriptions.push( + vscode.commands.registerCommand('extension.systemverilog.instantiateModule', + instantiateModuleInteract + ) + ); } -class SystemVerilogHoverProvider implements HoverProvider { +class SystemVerilogHoverProvider implements vscode.HoverProvider { private _excludedText: RegExp; constructor() { - this._excludedText = RegExp(/(reg|wire|input|output|logic|parameter|localparam|always|begin|end)/); + this._excludedText = RegExp(/\b(alias|always|always_comb|always_ff|always_latch|and|assert|assign|assume|automatic|before|begin|bind|bins|binsof|bit|break|buf|bufif0|bufif1|byte|case|casex|casez|cell|chandle|class|clocking|cmos|config|const|constraint|context|continue|cover|covergroup|coverpoint|cross|deassign|default|defparam|design|disable|dist|do|edge|else|end|endcase|endclass|endclocking|endconfig|endfunction|endgenerate|endgroup|endinterface|endmodule|endpackage|endprimitive|endprogram|endproperty|endspecify|endsequence|endtable|endtask|enum|event|expect|export|extends|extern|final|first_match|for|force|foreach|forever|fork|forkjoin|function|generate|genvar|highz0|highz1|if|iff|ifnone|ignore_bins|illegal_bins|import|incdir|include|initial|inout|input|inside|instance|int|integer|interface|intersect|join|join_any|join_none|large|liblist|library|local|localparam|logic|longint|macromodule|matches|medium|modport|module|nand|negedge|new|nmos|nor|noshowcancelled|not|notif0|notif1|null|or|output|package|packed|parameter|pmos|posedge|primitive|priority|program|property|protected|pull0|pull1|pulldown|pullup|pulsestyle_onevent|pulsestyle_ondetect|pure|rand|randc|randcase|randsequence|rcmos|real|realtime|ref|reg|release|repeat|return|rnmos|rpmos|rtran|rtranif0|rtranif1|scalared|sequence|shortint|shortreal|showcancelled|signed|small|solve|specify|specparam|static|string|strong0|strong1|struct|super|supply0|supply1|table|tagged|task|this|throughout|time|timeprecision|timeunit|tran|tranif0|tranif1|tri|tri0|tri1|triand|trior|trireg|type|typedef|union|unique|unsigned|use|uwire|var|vectored|virtual|void|wait|wait_order|wand|weak0|weak1|while|wildcard|wire|with|within|wor|xnor|xor)\b/); } public provideHover( - document: TextDocument, position: Position, token: CancellationToken): - Hover { - // text at current line - let textLine = document.lineAt(position).text; - + document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): + vscode.Hover { // get word start and end let textRange = document.getWordRangeAtPosition(position); // hover word - let targetText = textLine.substring(textRange.start.character, textRange.end.character); + let targetText = document.getText(textRange); if (targetText.search(this._excludedText) !== -1) { // systemverilog keywords return; } else { // find declaration let declarationText = this._findDeclaration(document, position, targetText); if (declarationText !== undefined) { - let renderedText = '```systemverilog\n' + declarationText + '\n```'; - return new Hover(renderedText); + return new vscode.Hover({language: 'systemverilog', value: declarationText}); } else { return; } } } - private _findDeclaration(document: TextDocument, position: Position, target: string): string { + private _findDeclaration(document: vscode.TextDocument, position: vscode.Position, target: string): string { // check target is valid variable name if (target.search(/[A-Za-z_][A-Za-z0-9_]*/g) === -1) { return; @@ -65,6 +70,10 @@ class SystemVerilogHoverProvider implements HoverProvider { for (let i = position.line-1; i >= 0; i--) { // text at current line let element = document.lineAt(i).text.replace(/\/\/.*/, '').trim().replace(/\s+/g, ' '); + let lastChar = element.charAt(element.length - 1); + if (lastChar === ',' || lastChar === ';') { // remove last ',' or ';' + element = element.substring(0, element.length - 1); + } // find variable declaration type if (element.search(regexVariableTypeStart) !== -1) { @@ -85,3 +94,134 @@ class SystemVerilogHoverProvider implements HoverProvider { } } } + +function getDirectories (srcpath: string): string[] { + return fs.readdirSync(srcpath) + .filter(file => fs.statSync(path.join(srcpath, file)).isDirectory()); +} + +function getFiles (srcpath: string): string[] { + return fs.readdirSync(srcpath) + .filter(file => fs.statSync(path.join(srcpath, file)).isFile()); +} + +function selectFile(currentDir?: string): Thenable { + currentDir = currentDir || vscode.workspace.rootPath; + + let dirs = getDirectories(currentDir); + // if is subdirectory, add '../' + if (currentDir !== vscode.workspace.rootPath) { + dirs.unshift('..') + } + // all files ends with '.sv' + let files = getFiles(currentDir) + .filter(file => file.endsWith('.v') || file.endsWith('.sv')); + + // available quick pick items + let items = dirs.concat(files); + + return vscode.window.showQuickPick(items) + .then(selected => { + if (!selected) { + return; + } + + // if is a directory + let location = path.join(currentDir, selected); + if (fs.statSync(location).isDirectory()) { + return selectFile(location); + } + + // return file path + return location; + }); +} + +function instantiatePort(ports: string[]): string { + let options = vscode.window.activeTextEditor.options; + let useTab = !options.insertSpaces; + let spacesSize = 4; + if (typeof options.tabSize === 'number') { + spacesSize = options.tabSize; + } + let port = '' + // .NAME(NAME) + for (let i = 0; i < ports.length; i++) { + let element = ports[i]; + if (useTab) { + port += `\t.${element}(${element})`; + } else { + port += ' '.repeat(spacesSize); + port += `.${element}(${element})`; + } + + if (i !== ports.length - 1) { + port += ','; + } + port += '\n'; + } + return port; +} + +function instantiateModule(srcpath: string) { + if (!srcpath || !fs.statSync(srcpath).isFile) { + return; + } + + // remove comment + let content = fs.readFileSync(srcpath, 'utf8').replace(/\/\/.*/g, '').replace(/\/\*[\s\S]*?\*\//g, ''); + if (content.indexOf('module') === -1) { + return; + } + // module 2001 style + let moduleIO = content.substring(content.indexOf('module'), content.indexOf(';')); + let moduleName = moduleIO.match(/module\s+\b([A-Za-z_][A-Za-z0-9_]*)\b/)[1]; + let parametersName = []; + let portsName = []; + let lines = moduleIO.split('\n'); + + // find all parameters and ports + lines.forEach(line => { + line = line.trim(); + let matched = line.match(/parameter\s+\b([A-Za-z_][A-Za-z0-9_]*)\b/); + if (matched !== null) { + parametersName.push(matched[1]); + } + + if (line.search(/^\b(input|output|inout)\b/) !== -1) { + let variables = line.replace(/\b(input|output|inout|reg|wire|logic|integer|bit|byte|shortint|int|longint|time|shortreal|real|double|realtime)\b/g, '') + .replace(/(\[.+?\])?/g, '').replace(/\s+/g, '').split(',').forEach(variable => { + if (variable) { + portsName.push(variable); + } + }); + } + }); + + if (portsName.length === 0) { + return; + } + let inst = moduleName + '\n'; + if (parametersName.length > 0) { + inst += '#(\n'; + inst += instantiatePort(parametersName); + inst += ')\n'; + } + inst += `u_${moduleName}(\n`; + inst += instantiatePort(portsName); + inst += ');\n'; + return inst; +} + +function instantiateModuleInteract() { + let filePath = path.dirname(vscode.window.activeTextEditor.document.fileName); + selectFile(filePath).then(srcpath => { + let inst = instantiateModule(srcpath); + + let editor = vscode.window.activeTextEditor; + let sel = editor.selection; + editor.edit(editBuilder => { + editBuilder.insert(sel.start, inst); + }); + }); +}