-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Start supporting basic hover and definitions
- Loading branch information
David Gileadi
committed
Oct 17, 2016
1 parent
a7f537a
commit 83e3401
Showing
12 changed files
with
377 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"env": { | ||
"browser": false, | ||
"commonjs": true, | ||
"es6": true, | ||
"node": true | ||
}, | ||
"parserOptions": { | ||
"ecmaFeatures": { | ||
"jsx": true | ||
}, | ||
"sourceType": "module" | ||
}, | ||
"extends": ["esnext"], | ||
"rules": { | ||
"no-const-assign": "warn", | ||
"no-this-before-super": "warn", | ||
"no-undef": "warn", | ||
"no-unreachable": "warn", | ||
"no-unused-vars": "warn", | ||
"constructor-super": "warn", | ||
"valid-typeof": "warn" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// A launch configuration that launches the extension inside a new window | ||
{ | ||
"version": "0.1.0", | ||
"configurations": [ | ||
{ | ||
"name": "Launch Extension", | ||
"type": "extensionHost", | ||
"request": "launch", | ||
"runtimeExecutable": "${execPath}", | ||
"args": ["--extensionDevelopmentPath=${workspaceRoot}" ], | ||
"stopOnEntry": false | ||
}, | ||
{ | ||
"name": "Test", | ||
"type": "extensionHost", | ||
"request": "launch", | ||
"runtimeExecutable": "${execPath}", | ||
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/test" ], | ||
"stopOnEntry": false | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// Place your settings in this file to overwrite default and user settings. | ||
{ | ||
"typescript.tsdk": "./node_modules/typescript/lib" // we want to use the TS server from our node_modules folder to control its version | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
.vscode/** | ||
.vscode-test/** | ||
test/** | ||
.gitignore | ||
jsconfig.json | ||
vsc-extension-quickstart.md | ||
.eslintrc.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
let vscode = require('vscode'); | ||
let MumpsHoverProvider = require('./mumps-hover-provider').MumpsHoverProvider; | ||
let MumpsDefinitionProvider = require('./mumps-definition-provider').MumpsDefinitionProvider; | ||
|
||
function activate(context) { | ||
context.subscriptions.push( | ||
vscode.languages.registerHoverProvider( | ||
'mumps', new MumpsHoverProvider())); | ||
context.subscriptions.push( | ||
vscode.languages.registerDefinitionProvider( | ||
'mumps', new MumpsDefinitionProvider())); | ||
} | ||
exports.activate = activate; | ||
|
||
function deactivate() { | ||
} | ||
exports.deactivate = deactivate; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"compilerOptions": { | ||
"module": "commonjs", | ||
"target": "es6", | ||
"lib": [ | ||
"es6" | ||
] | ||
}, | ||
"exclude": [ | ||
"node_modules" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
[ | ||
{ "name": "BREAK", "type": "command", "abbreviation": "B", "description": "suspends execution or exits a block (non- standard extension)" }, | ||
{ "name": "CLOSE", "type": "command", "abbreviation": "C", "description": "release an I/O device" }, | ||
{ "name": "DATABASE", "type": "command", "description": "set global array database (non-standard extension)" }, | ||
{ "name": "DO", "type": "command", "abbreviation": "D", "description": "execute a program, section of code or block" }, | ||
{ "name": "ELSE", "type": "command", "abbreviation": "E", "description": "conditional execution based on $test" }, | ||
{ "name": "FOR", "type": "command", "abbreviation": "F", "description": "iterative execution of a line or block" }, | ||
{ "name": "GOTO", "type": "command", "abbreviation": "G", "description": "transfer of control to a label or program" }, | ||
{ "name": "HALT", "type": "command", "abbreviation": "H", "description": "terminate execution" }, | ||
{ "name": "HANG", "type": "command", "abbreviation": "H", "description": "delay execution for a specified period of time" }, | ||
{ "name": "HTML", "type": "command", "description": "write line to web server (non-standard extension)" }, | ||
{ "name": "IF", "type": "command", "abbreviation": "I", "description": "conditional execution of remainder of line" }, | ||
{ "name": "JOB", "type": "command", "abbreviation": "J", "description": "create an independent process" }, | ||
{ "name": "LOCK", "type": "command", "abbreviation": "L", "description": "exclusive access/release named resource" }, | ||
{ "name": "KILL", "type": "command", "abbreviation": "K", "description": "delete a local or global variable" }, | ||
{ "name": "MERGE", "type": "command", "abbreviation": "M", "description": "copy arrays" }, | ||
{ "name": "NEW", "type": "command", "abbreviation": "N", "description": "create new copies of local variables" }, | ||
{ "name": "OPEN", "type": "command", "abbreviation": "O", "description": "obtain ownership of a device" }, | ||
{ "name": "QUIT", "type": "command", "abbreviation": "Q", "description": "end a for loop or exit a block" }, | ||
{ "name": "READ", "type": "command", "abbreviation": "R", "description": "read from a device" }, | ||
{ "name": "SET", "type": "command", "abbreviation": "S", "description": "assign a value to a global or local variable" }, | ||
{ "name": "SHELL", "type": "command", "description": "execute a command shell (non-standard extension)" }, | ||
{ "name": "SQL", "type": "command", "description": "execute an SQL statement (non-standard extension)" }, | ||
{ "name": "TCOMMIT", "type": "command", "abbreviation": "TC", "description": "commit a transaction" }, | ||
{ "name": "TRESTART", "type": "command", "abbreviation": "TRE", "description": "roll back / restart a transaction" }, | ||
{ "name": "TROLLBACK", "type": "command", "abbreviation": "TRO", "description": "roll back a transaction" }, | ||
{ "name": "TSTART", "type": "command", "abbreviation": "TS", "description": "begin a transaction" }, | ||
{ "name": "USE", "type": "command", "abbreviation": "U", "description": "select which device to read/write" }, | ||
{ "name": "VIEW", "type": "command", "abbreviation": "V", "description": "implementation defined" }, | ||
{ "name": "WRITE", "type": "command", "abbreviation": "W", "description": "write to device" }, | ||
{ "name": "XECUTE", "type": "command", "abbreviation": "X", "description": "dynamically execute strings" }, | ||
|
||
{ "name": "$DEVICE", "type": "variable", "abbreviation": "$D", "description": "status of current device" }, | ||
{ "name": "$ECODE", "type": "variable", "abbreviation": "$EC", "description": "list of error codes" }, | ||
{ "name": "$ESTACK", "type": "variable", "abbreviation": "$ES", "description": "number of stack levels" }, | ||
{ "name": "$ETRAP", "type": "variable", "abbreviation": "$ET", "description": "code to execute on error" }, | ||
{ "name": "$HOROLOG", "type": "variable", "abbreviation": "$H", "description": "days,seconds time stamp" }, | ||
{ "name": "$IO", "type": "variable", "abbreviation": "$I", "description": "current IO unit" }, | ||
{ "name": "$JOB", "type": "variable", "abbreviation": "$J", "description": "current process ID" }, | ||
{ "name": "$KEY", "type": "variable", "abbreviation": "$K", "description": "read command control code" }, | ||
{ "name": "$PRINCIPAL", "type": "variable", "abbreviation": "$P", "description": "principal IO device" }, | ||
{ "name": "$QUIT", "type": "variable", "abbreviation": "$Q", "description": "indicates how current process invoked." }, | ||
{ "name": "$STACK", "type": "variable", "abbreviation": "$ST", "description": "current process stack level" }, | ||
{ "name": "$STORAGE", "type": "variable", "abbreviation": "$S", "description": "amount of memory available" }, | ||
{ "name": "$SYSTEM", "type": "variable", "abbreviation": "$SY", "description": "system ID" }, | ||
{ "name": "$TEST", "type": "variable", "abbreviation": "$T", "description": "result of prior operation" }, | ||
{ "name": "$TLEVEL", "type": "variable", "abbreviation": "$TL", "description": "number transactions in process" }, | ||
{ "name": "$TRESTART", "type": "variable", "abbreviation": "$TR", "description": "number of restarts on current transaction" }, | ||
{ "name": "$X", "type": "variable", "abbreviation": "$X", "description": "position of horizontal cursor" }, | ||
{ "name": "$Y", "type": "variable", "abbreviation": "$Y", "description": "position of vertical cursor" }, | ||
|
||
{ "name": "$ASCII", "type": "function", "abbreviation": "$A", "description": "ASCII numeric code of a character", "parameters": [ | ||
{ "name": "VALUE", "type": "string", "description": "A string to get a code from" }, | ||
{ "name": "POS", "type": "number", "description": "(optional) The 1-based position of the character in VALUE; defaults to 1" } | ||
], "returns": "The ASCII numeric code or `-1` if not found" }, | ||
{ "name": "$CHAR", "type": "function", "abbreviation": "$C", "description": "ASCII character from numeric code" }, | ||
{ "name": "$DATA", "type": "function", "abbreviation": "$D", "description": "determines variable's definition" }, | ||
{ "name": "$EXTRACT", "type": "function", "abbreviation": "$E", "description": "extract a substring" }, | ||
{ "name": "$FIND", "type": "function", "abbreviation": "$F", "description": "find a substring" }, | ||
{ "name": "$FNUMBER", "type": "function", "abbreviation": "$FN", "description": "format a number" }, | ||
{ "name": "$GET", "type": "function", "abbreviation": "$G", "description": "get default or actual value" }, | ||
{ "name": "$JUSTIFY", "type": "function", "abbreviation": "$J", "description": "format a number or string" }, | ||
{ "name": "$LENGTH", "type": "function", "abbreviation": "$L", "description": "determine string length" }, | ||
{ "name": "$NAME", "type": "function", "abbreviation": "$NA", "description": "evaluate array reference" }, | ||
{ "name": "$ORDER", "type": "function", "abbreviation": "$O", "description": "find next or previous node" }, | ||
{ "name": "$PIECE", "type": "function", "abbreviation": "$P", "description": "extract substring based on pattern" }, | ||
{ "name": "$QLENGTH", "type": "function", "abbreviation": "$QL", "description": "number of subscripts in an array reference" }, | ||
{ "name": "$QSUBSCRIPT", "type": "function", "abbreviation": "$QS", "description": "value of specified subscript" }, | ||
{ "name": "$QUERY", "type": "function", "abbreviation": "$Q", "description": "next array reference" }, | ||
{ "name": "$RANDOM", "type": "function", "abbreviation": "$R", "description": "random number" }, | ||
{ "name": "$REVERSE", "type": "function", "abbreviation": "$RE", "description": "string in reverse order" }, | ||
{ "name": "$SELECT", "type": "function", "abbreviation": "$S", "description": "value of first true argument" }, | ||
{ "name": "$STACK", "type": "function", "abbreviation": "$ST", "description": "stack information" }, | ||
{ "name": "$TEXT", "type": "function", "abbreviation": "$T", "description": "string containing a line of code" }, | ||
{ "name": "$TRANSLATE", "type": "function", "abbreviation": "$TR", "description": "translate characters in a string" }, | ||
{ "name": "$VIEW", "type": "function", "abbreviation": "$V", "description": "implementation defined" } | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
let fs = require('fs'); | ||
let path = require('path'); | ||
let vscode = require('vscode'); | ||
let Location = vscode.Location; | ||
let Position = vscode.Position; | ||
let Uri = vscode.Uri; | ||
let MumpsToken = require('./mumps-language-token').MumpsToken; | ||
const EXTENSIONS = ['.M', '.INT', '.ZWR', '.m', '.int', '.zwr']; | ||
|
||
class MumpsDefinitionProvider { | ||
provideDefinition(document, position) { | ||
let token = new MumpsToken(document, position); | ||
if (token.isLabelReference) { | ||
let uri = token.labelProgram ? | ||
siblingUri(document, token.labelProgram) : | ||
document.uri; | ||
let labelPosition = token.label ? | ||
findLabelPositionInFile(uri, token.label, token.labelOffset) : | ||
token.labelOffset ? | ||
document.positionAt(token.labelOffset) : | ||
new Position(0, 0); | ||
return new Location(uri, labelPosition); | ||
} | ||
} | ||
} | ||
exports.MumpsDefinitionProvider = MumpsDefinitionProvider; | ||
|
||
function siblingUri(document, fileName) { | ||
let siblingPath = path.resolve(document.uri.fsPath, '../' + fileName); | ||
if (!fs.existsSync(siblingPath)) { | ||
for (var extension of EXTENSIONS) { | ||
let extendedPath = siblingPath + extension; | ||
if (fs.existsSync(extendedPath)) { | ||
siblingPath = extendedPath; | ||
break; | ||
} | ||
} | ||
} | ||
return Uri.file(siblingPath); | ||
} | ||
|
||
function findLabelPositionInFile(uri, label, offset) { | ||
let line = offset || 0; | ||
if (label) { | ||
try { | ||
let text = fs.readFileSync(uri.fsPath, 'utf8'); | ||
let labelRe = new RegExp('^' + label + '[ \t\\()]', 'm'); | ||
let result = labelRe.exec(text); | ||
if (result) { | ||
line += countLines(text, result.index); | ||
} | ||
} catch (e) {} | ||
} | ||
return new Position(line, 0); | ||
} | ||
|
||
function countLines(text, index) { | ||
if (index >= text.length) { | ||
index = text.length - 1; | ||
} | ||
let re = /[\r\n]+/g; | ||
let result; | ||
let line = 0; | ||
while ((result = re.exec(text)) && result.index >= 0 && result.index < index) { | ||
++line; | ||
} | ||
return line; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
let vscode = require('vscode'); | ||
let Hover = vscode.Hover; | ||
let MumpsToken = require('./mumps-language-token').MumpsToken; | ||
const definitions = require('./language-definitions.json'); | ||
|
||
const definitionsByName = {}; | ||
for (var definition of definitions) { | ||
addDefinition(definition.name, definition); | ||
if (definition.abbreviation) { | ||
addDefinition(definition.abbreviation, definition); | ||
} | ||
} | ||
|
||
function addDefinition(name, definition) { | ||
if (!definitionsByName[name]) { | ||
definitionsByName[name] = [definition]; | ||
} else { | ||
definitionsByName[name].push(definition); | ||
} | ||
} | ||
|
||
class MumpsHoverProvider { | ||
provideHover(document, position) { | ||
let token = new MumpsToken(document, position); | ||
|
||
if (!token.mayBeCommand && !token.isIntrinsic) { | ||
return; | ||
} | ||
|
||
let definitions = definitionsByName[token.word.toUpperCase()]; | ||
if (definitions) { | ||
for (definition of definitions) { | ||
if (token.isFunctionCall && definition.type !== 'function') { | ||
continue; | ||
} | ||
var markdown = '**' + definition.name + '**: ' + definition.description; | ||
// TODO: function parameters | ||
return new Hover(markdown, token.range); | ||
} | ||
} | ||
} | ||
} | ||
exports.MumpsHoverProvider = MumpsHoverProvider; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
let vscode = require('vscode'); | ||
let Position = vscode.Position; | ||
let Range = vscode.Range; | ||
|
||
class MumpsToken { | ||
constructor(document, position) { | ||
this.document = document; | ||
this.position = position; | ||
|
||
this.range = document.getWordRangeAtPosition(position); | ||
if (!this.range) { | ||
return; | ||
} | ||
|
||
this.word = document.getText(this.range); | ||
if (!this.word) { | ||
return; | ||
} | ||
|
||
this.surroundWord = getWordWithSurrounds(document, this.range) || this.word; | ||
|
||
if (this.isIntrinsic) { | ||
this.word = '$' + this.word; | ||
} | ||
} | ||
|
||
get mayBeCommand() { | ||
if (!this.surroundWord) { | ||
return false; | ||
} | ||
var lastChar = this.surroundWord.charAt(this.surroundWord.length - 1); | ||
return isWhitespace(this.surroundWord.charAt(0)) && | ||
(lastChar === ':' || | ||
isWhitespace(lastChar) || | ||
this.surroundWord.length === this.word.length + 1); // end-of-line | ||
} | ||
|
||
get isIntrinsic() { | ||
if (!this.surroundWord) { | ||
return false; | ||
} | ||
return this.surroundWord.charAt(0) === '$'; | ||
} | ||
|
||
get isFunctionCall() { | ||
if (!this.surroundWord) { | ||
return false; | ||
} | ||
return this.surroundWord.charAt(this.surroundWord.length - 1) === '('; | ||
} | ||
|
||
get isLabelReference() { | ||
if (this._isLabelReference === undefined) { | ||
let line = this.document.lineAt(this.range.start); | ||
let regex = new RegExp('[ \t](D|DO|G|GOTO)[ \t]+([%\\^\\+A-Z0-9]*' + this.word + '[%\\^\\+A-Z0-9]*)', 'i'); | ||
let match = regex.exec(line.text); | ||
this._isLabelReference = match !== null; | ||
if (match) { | ||
let fullLabel = match[2]; | ||
let partsRegex = /([%A-Z][%A-Z0-9]*)?(\+\d+)?(\^[%A-Z][%A-Z0-9]*)?/gi; | ||
let parts = partsRegex.exec(fullLabel); | ||
this.label = parts[1]; | ||
this.labelOffset = withoutFirstCharacter(parts[2]); | ||
this.labelProgram = withoutFirstCharacter(parts[3]); | ||
} | ||
} | ||
return this._isLabelReference; | ||
} | ||
} | ||
exports.MumpsToken = MumpsToken; | ||
|
||
function getWordWithSurrounds(document, range) { | ||
if (range.start.character <= 0) { | ||
return; | ||
} | ||
let start = new Position(range.start.line, range.start.character - 1); | ||
let end = new Position(range.end.line, range.end.character + 1); | ||
return document.getText(new Range(start, end)); | ||
} | ||
|
||
function isWhitespace(char) { | ||
return /\s+/.test(char); | ||
} | ||
|
||
function withoutFirstCharacter(string) { | ||
return string ? string.substring(1) : string; | ||
} |
Oops, something went wrong.