Skip to content

Commit

Permalink
Merge pull request #38 from chrispahm/add-lsp-features
Browse files Browse the repository at this point in the history
fAdd lsp features
  • Loading branch information
chrispahm authored Oct 15, 2023
2 parents be7fbc3 + 33792a1 commit 76771a3
Show file tree
Hide file tree
Showing 11 changed files with 269 additions and 23 deletions.
25 changes: 5 additions & 20 deletions extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ const getSymbolUnderCursor = require("./src/getSymbolUnderCursor");
const getGamsIdeViewContainerContent = require("./src/utils/getGamsIdeViewContainerContent");
const getGamsIdeDataViewContainerContent = require("./src/utils/getGamsIdeDataViewContainerContent");
const debouncedListenToLstFiles = require("./src/parseLstFiles");
const provideGAMSCompletionItems = require("./src/provideGAMSCompletionItems");
const provideGAMSSignatureHelp = require("./src/provideGAMSSignatureHelp");
const updateStatusBar = require("./src/utils/updateStatusBar");
const checkIfExcluded = require("./src/utils/checkIfExcluded");
const implementLSPMethods = require("./src/lsp/implementLSPMethods");
const State = require("./src/State.js");

let terminal;
Expand Down Expand Up @@ -85,6 +84,10 @@ async function activate(context) {
})
);

// add LSP features
implementLSPMethods(context, state);

// add commands
// register a command to get the symbol under the cursor
context.subscriptions.push(
vscode.window.onDidChangeTextEditorSelection(async (event) => {
Expand All @@ -94,24 +97,6 @@ async function activate(context) {
})
);

// provide auto-completeion
context.subscriptions.push(
vscode.languages.registerCompletionItemProvider("gams", {
provideCompletionItems(document, position) {
return provideGAMSCompletionItems(document, position, state);
}
}, " ", ",", "(", "[", "{", ":", ">", "<", "=", "+", "-", "*", "/", "^", "!", "&", "|", ">", "<", "\t")
);

// provide signature help
context.subscriptions.push(
vscode.languages.registerSignatureHelpProvider("gams", {
provideSignatureHelp(document, position) {
return provideGAMSSignatureHelp(document, position, state);
}
}, "(", ",")
);

// register status bar item showing the current main file
gamsStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
context.subscriptions.push(gamsStatusBarItem);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "gams-ide",
"displayName": "gams-ide",
"description": "A GAMS integrated development environment for VSCode",
"version": "0.0.61",
"version": "0.0.7",
"repository": {
"type": "git",
"url": "https://github.com/chrispahm/gams-ide"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const gamsParser = require("./utils/gamsParser.js");
const gamsParser = require("../utils/gamsParser.js");
const vscode = require("vscode");

function getCompletionStringsSubsets(symbol) {
Expand Down
17 changes: 17 additions & 0 deletions src/lsp/gmsDeclarationProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const vscode = require("vscode");

module.exports = function gmsDeclarationProvider(document, position, state) {
// get the word at the current position
const wordRange = document.getWordRangeAtPosition(position);
const word = document.getText(wordRange);
const referenceTree = state.get("referenceTree");
let matchingRef = referenceTree?.find(
(item) => item.name?.toLowerCase() === word?.toLowerCase()
);

if (matchingRef && matchingRef.declared) {
const { file, line, column } = matchingRef.declared;
const uri = vscode.Uri.file(file);
return new vscode.Location(uri, new vscode.Position(line - 1, column - 1));
}
};
17 changes: 17 additions & 0 deletions src/lsp/gmsDefinitionProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const vscode = require("vscode");

module.exports = function gmsDefinitionProvider(document, position, state) {
// get the word at the current position
const wordRange = document.getWordRangeAtPosition(position);
const word = document.getText(wordRange);
const referenceTree = state.get("referenceTree");
let matchingRef = referenceTree?.find(
(item) => item.name?.toLowerCase() === word?.toLowerCase()
);

if (matchingRef && matchingRef.defined) {
const { file, line, column } = matchingRef.defined;
const uri = vscode.Uri.file(file);
return new vscode.Location(uri, new vscode.Position(line-1, column-1));
}
};
59 changes: 59 additions & 0 deletions src/lsp/gmsDocumentSymbolsProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const vscode = require("vscode");

function createDocumentSymbol(symbol, detail) {
let symbolKind = vscode.SymbolKind.Variable;
if (symbol.type === "SET") {
symbolKind = vscode.SymbolKind.Array;
} else if (symbol.type === "EQU") {
symbolKind = vscode.SymbolKind.Function;
} else if (symbol.type === "PARAM") {
symbolKind = vscode.SymbolKind.Constant;
} else if (symbol.type === "MODEL") {
symbolKind = vscode.SymbolKind.Class;
}
const symbolRange = new vscode.Range(
new vscode.Position(symbol.line-1, symbol.column-1),
new vscode.Position(symbol.line-1, symbol.column)
);
return new vscode.DocumentSymbol(
symbol.name,
detail,
symbolKind,
symbolRange,
symbolRange
);
}

// Implements "go to document symbols"
module.exports = function gmsDocumentSymbolsProvider(document, state) {
const referenceTree = state.get("referenceTree");
const symbolsInFile = referenceTree.reduce((acc, symbol) => {
// check if the smybol is declared, defined, references or assigned
// in this file
if (!symbol || !symbol.name) return acc;
const { declared, defined, ref, assigned } = symbol;
if (declared?.file === document.fileName) {
// add to symbols
acc.push(createDocumentSymbol(symbol, "declared"));
}
if (defined?.file === document.fileName) {
// add to symbols
acc.push(createDocumentSymbol(symbol, "defined"));
}
ref?.forEach((ref) => {
if (ref.file === document.fileName) {
// add to symbols
acc.push(createDocumentSymbol(symbol, "referenced"));
}
});
assigned?.forEach((ref) => {
if (ref.file === document.fileName) {
// add to symbols
acc.push(createDocumentSymbol(symbol, "assigned"));
}
});

return acc;
}, []);
return symbolsInFile;
};
22 changes: 22 additions & 0 deletions src/lsp/gmsHoverProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const vscode = require("vscode");

module.exports = function gmsHoverProvider(document, position, state) {
// get the word at the current position
const wordRange = document.getWordRangeAtPosition(position);
const word = document.getText(wordRange);
const referenceTree = state.get("referenceTree");
let matchingRef = referenceTree?.find(
(item) => item.name?.toLowerCase() === word?.toLowerCase()
);

if (matchingRef) {
let hoverContent = new vscode.MarkdownString(`(${matchingRef.type.toLowerCase()}) **${matchingRef.name}**${matchingRef.description ? "\n\n" + matchingRef.description : ""}`);
if (matchingRef.domain) {
hoverContent = new vscode.MarkdownString(`(${matchingRef.type.toLowerCase()}) **${matchingRef.name}**(${matchingRef.domain.map(d => d.name).join(", ")})${matchingRef.description ? "\n\n" + matchingRef.description : ""}`);
}
return new vscode.Hover(
hoverContent,
wordRange
);
}
};
20 changes: 20 additions & 0 deletions src/lsp/gmsImplementationProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const vscode = require("vscode");

// Implements "go to implementations"
module.exports = function gmsReferenceProvider(document, position, state) {
// get the word at the current position
const wordRange = document.getWordRangeAtPosition(position);
const word = document.getText(wordRange);
const referenceTree = state.get("referenceTree");
let matchingRef = referenceTree?.find(
(item) => item.name?.toLowerCase() === word?.toLowerCase()
);

if (matchingRef && matchingRef.assigned) {
return matchingRef.assigned.map((ref) => {
const { file, line, column } = ref;
const uri = vscode.Uri.file(file);
return new vscode.Location(uri, new vscode.Position(line-1, column-1));
});
}
};
20 changes: 20 additions & 0 deletions src/lsp/gmsReferenceProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const vscode = require("vscode");

// Implements "go to references"
module.exports = function gmsReferenceProvider(document, position, state) {
// get the word at the current position
const wordRange = document.getWordRangeAtPosition(position);
const word = document.getText(wordRange);
const referenceTree = state.get("referenceTree");
let matchingRef = referenceTree?.find(
(item) => item.name?.toLowerCase() === word?.toLowerCase()
);

if (matchingRef && matchingRef.ref) {
return matchingRef.ref.map((ref) => {
const { file, line, column } = ref;
const uri = vscode.Uri.file(file);
return new vscode.Location(uri, new vscode.Position(line-1, column-1));
});
}
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const vscode = require("vscode");
const gamsParser = require('./utils/gamsParser.js');
const gamsParser = require('../utils/gamsParser.js');

module.exports = function provideGAMSSignatureHelp(document, position, state) {
const referenceTree = state.get("referenceTree");
Expand Down
106 changes: 106 additions & 0 deletions src/lsp/implementLSPMethods.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
const vscode = require("vscode");
const gmsDeclarationProvider = require("./gmsDeclarationProvider.js");
const gmsDefinitionProvider = require("./gmsDefinitionProvider.js");
const gmsHoverProvider = require("./gmsHoverProvider.js");
const gmsReferenceProvider = require("./gmsReferenceProvider.js");
const gmsImplementationProvider = require("./gmsImplementationProvider.js");
const gmsDocumentSymbolsProvider = require("./gmsDocumentSymbolsProvider.js");
const gmsCompletionItemsProvider = require("./gmsCompletionItemsProvider.js");
const gmsSignatureHelpProvider = require("./gmsSignatureHelpProvider.js");

module.exports = function implementLSPMethods(context, state) {
// Register the GAMS declaration provider
context.subscriptions.push(
vscode.languages.registerDeclarationProvider(
{ scheme: "file", language: "gams" },
{
provideDeclaration: (document, position) => {
return gmsDeclarationProvider(document, position, state);
},
}
)
);

// Register the GAMS definition provider
context.subscriptions.push(
vscode.languages.registerDefinitionProvider(
{ scheme: "file", language: "gams" },
{
provideDefinition: (document, position) => {
return gmsDefinitionProvider(document, position, state);
},
}
)
);

// Register the GAMS hover provider
context.subscriptions.push(
vscode.languages.registerHoverProvider(
{ scheme: "file", language: "gams" },
{
provideHover: (document, position) => {
return gmsHoverProvider(document, position, state);
},
}
)
);

// Register the GAMS reference provider
context.subscriptions.push(
vscode.languages.registerReferenceProvider(
{ scheme: "file", language: "gams" },
{
provideReferences: (document, position) => {
return gmsReferenceProvider(document, position, state);
},
}
)
);

// Register the GAMS implementation provider
context.subscriptions.push(
vscode.languages.registerImplementationProvider(
{ scheme: "file", language: "gams" },
{
provideImplementation: (document, position) => {
return gmsImplementationProvider(document, position, state);
},
}
)
);

// Register the GAMS document symbol provider
/*
TODO: VSCode seems to query symbols quite often, so this can be
a performance bottleneck. Caching symbols may not make sense, as they have
to be re-calculated when the source code changes.
context.subscriptions.push(
vscode.languages.registerDocumentSymbolProvider(
{ scheme: "file", language: "gams" },
{
provideDocumentSymbols: (document) => {
return gmsDocumentSymbolsProvider(document, state);
},
}
)
);
*/
// provide auto-completeion
context.subscriptions.push(
vscode.languages.registerCompletionItemProvider("gams", {
provideCompletionItems(document, position) {
return gmsCompletionItemsProvider(document, position, state);
}
}, " ", ",", "(", "[", "{", ":", ">", "<", "=", "+", "-", "*", "/", "^", "!", "&", "|", ">", "<", "\t")
);

// provide signature help
context.subscriptions.push(
vscode.languages.registerSignatureHelpProvider("gams", {
provideSignatureHelp(document, position) {
return gmsSignatureHelpProvider(document, position, state);
}
}, "(", ",")
);
};

0 comments on commit 76771a3

Please sign in to comment.