feat(server): Support show types on hover [LNG-285] (#64)

This commit is contained in:
Dima 2023-12-08 15:45:52 +07:00 committed by GitHub
parent fdb17c9266
commit 41fd59fe31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 112 additions and 34 deletions

6
package-lock.json generated
View File

@ -28,6 +28,12 @@
"vscode": "^1.50.0"
}
},
"../aqua/language-server/language-server-npm": {
"name": "@fluencelabs/aqua-language-server-api",
"version": "0.13.0",
"extraneous": true,
"license": "Apache-2.0"
},
"node_modules/@eslint/eslintrc": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.3.tgz",

View File

@ -8,7 +8,7 @@
"name": "aqua-ls-server",
"version": "0.0.1",
"dependencies": {
"@fluencelabs/aqua-language-server-api": "0.13.0",
"@fluencelabs/aqua-language-server-api": "0.13.1",
"global-dirs": "^3.0.0",
"vscode-languageserver": "^7.0.0",
"vscode-languageserver-textdocument": "^1.0.4"
@ -30,9 +30,9 @@
"license": "Apache-2.0"
},
"node_modules/@fluencelabs/aqua-language-server-api": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@fluencelabs/aqua-language-server-api/-/aqua-language-server-api-0.13.0.tgz",
"integrity": "sha512-NWrKnDAwc7GjIFbo1ph1AdydakIkvJC19Euh+1NnLMBZVgpSJ1nP28ZhzlbvMeYiwrJ2P/ZKy8ZahmsvUl4IaQ=="
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/@fluencelabs/aqua-language-server-api/-/aqua-language-server-api-0.13.1.tgz",
"integrity": "sha512-RipKk2NZiwX72eEN5gfDbdzljsGGMva01sCukrdrxV+zdyh+9TVZCjlpaVwZsjW/5e1wRCk0DE+RbVTlDc3Ktg=="
},
"node_modules/global-dirs": {
"version": "3.0.0",
@ -97,9 +97,9 @@
},
"dependencies": {
"@fluencelabs/aqua-language-server-api": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@fluencelabs/aqua-language-server-api/-/aqua-language-server-api-0.13.0.tgz",
"integrity": "sha512-NWrKnDAwc7GjIFbo1ph1AdydakIkvJC19Euh+1NnLMBZVgpSJ1nP28ZhzlbvMeYiwrJ2P/ZKy8ZahmsvUl4IaQ=="
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/@fluencelabs/aqua-language-server-api/-/aqua-language-server-api-0.13.1.tgz",
"integrity": "sha512-RipKk2NZiwX72eEN5gfDbdzljsGGMva01sCukrdrxV+zdyh+9TVZCjlpaVwZsjW/5e1wRCk0DE+RbVTlDc3Ktg=="
},
"global-dirs": {
"version": "3.0.0",

View File

@ -11,12 +11,12 @@
"url": "https://github.com/fluencelabs/aqua"
},
"dependencies": {
"@fluencelabs/aqua-language-server-api": "0.13.0",
"@fluencelabs/aqua-language-server-api": "0.13.1",
"global-dirs": "^3.0.0",
"vscode-languageserver": "^7.0.0",
"vscode-languageserver-textdocument": "^1.0.4"
},
"scripts": {
"get-root": "npm root --globall"
"get-root": "npm root --global"
}
}

49
server/src/search.ts Normal file
View File

@ -0,0 +1,49 @@
import type { TokenInfo, TokenLink, TokenLocation } from '@fluencelabs/aqua-language-server-api/aqua-lsp-api';
import type { Position } from 'vscode-languageserver-textdocument';
function isToken(location: TokenLocation, position: Position, name: string) {
return (
location.name == name &&
location.startLine <= position.line &&
location.startCol <= position.character &&
location.endLine >= position.line &&
location.endCol >= position.character
);
}
function isTokenByLocation(location: TokenLocation, locationRight: TokenLocation) {
return (
location.startLine <= locationRight.startLine &&
location.startCol <= locationRight.startCol &&
location.endLine >= locationRight.endLine &&
location.endCol >= locationRight.endCol
);
}
export function searchDefinition(position: Position, name: string, locations: TokenLink[]): TokenLink | undefined {
return locations.find((token) => isToken(token.current, position, name));
}
// find a token by position in the definition.
// If there is no token, look for the definition token in the locations,
// and then look up the token information from the token found.
export function searchInfo(
position: Position,
name: string,
tokens: TokenInfo[],
locations: TokenLink[],
): TokenInfo | undefined {
const tokenInfo = tokens.find((token) => isToken(token.location, position, name));
if (tokenInfo) {
return tokenInfo;
}
const tokenLink = searchDefinition(position, name, locations);
if (tokenLink) {
return tokens.find((token) => isTokenByLocation(token.location, tokenLink.definition));
} else {
return undefined;
}
}

View File

@ -9,14 +9,15 @@ import {
TextDocumentSyncKind,
} from 'vscode-languageserver/node';
import type { WorkspaceFolder } from 'vscode-languageserver-protocol';
import { Position, TextDocument } from 'vscode-languageserver-textdocument';
import type { DefinitionParams, Location } from 'vscode-languageserver';
import type { TokenLink } from '@fluencelabs/aqua-language-server-api/aqua-lsp-api';
import { TextDocument } from 'vscode-languageserver-textdocument';
import type { DefinitionParams, Hover, HoverParams, Location, MarkupContent } from 'vscode-languageserver';
import { MarkupKind } from 'vscode-languageserver';
import type { TokenInfo, TokenLink } from '@fluencelabs/aqua-language-server-api/aqua-lsp-api';
import { compileAqua } from './validation';
import { FluenceCli } from './cli';
import { Settings, SettingsManager } from './settings';
import { searchDefinition, searchInfo } from './search';
// Create a connection to the server, using Node's IPC as a transport.
// Also include all preview / proposed LSP features.
@ -37,37 +38,52 @@ function createSettingsManager(cliPath?: string, defaultSettings?: Settings): Se
let documentSettings = createSettingsManager();
function searchDefinition(position: Position, name: string, locations: TokenLink[]): TokenLink | undefined {
return locations.find(
(token) =>
token.current.name == name &&
token.current.startLine <= position.line &&
token.current.startCol <= position.character &&
token.current.endLine >= position.line &&
token.current.endCol >= position.character,
);
interface PageInfo {
links: TokenLink[];
tokens: TokenInfo[];
}
// Cache all locations of all open documents
const allLocations: Map<string, TokenLink[]> = new Map();
const allPageInfo: Map<string, PageInfo> = new Map();
function onHover({ textDocument, position }: HoverParams): Hover | null {
const doc = documents.get(textDocument.uri);
const currentPage = allPageInfo.get(textDocument.uri);
if (doc == undefined || currentPage == undefined) {
throw new Error(`Cannot find compilation info about page: ${textDocument.uri}`);
}
const token = searchInfo(position, doc.uri.replace('file://', ''), currentPage.tokens, currentPage.links);
if (token) {
const content: MarkupContent = { kind: MarkupKind.PlainText, value: token.type };
const hover: Hover = { contents: content };
return hover;
}
return null;
}
connection.onHover(onHover);
async function onDefinition({ textDocument, position }: DefinitionParams): Promise<Location[]> {
connection.console.log('onDefinition event');
const doc = documents.get(textDocument.uri);
const currentLocations = allLocations.get(textDocument.uri);
const currentPage = allPageInfo.get(textDocument.uri);
if (doc == undefined || currentLocations == undefined) {
if (doc == undefined || currentPage == undefined) {
return [];
}
const token = searchDefinition(position, doc.uri.replace('file://', ''), currentLocations);
connection.console.log('found token: ' + JSON.stringify(token));
const tokenLink = searchDefinition(position, doc.uri.replace('file://', ''), currentPage.links);
if (token == undefined) {
if (tokenLink == undefined) {
return [];
}
const definition = token.definition;
const definition = tokenLink.definition;
return [
{
@ -117,6 +133,7 @@ connection.onInitialize((params: InitializeParams) => {
capabilities: {
textDocumentSync: TextDocumentSyncKind.Full,
definitionProvider: true,
hoverProvider: true,
},
};
@ -163,9 +180,9 @@ async function validateDocument(textDocument: TextDocument): Promise<void> {
connection.console.log(`validateDocument ${textDocument.uri} with settings ${JSON.stringify(settings)}`);
const [diagnostics, locations] = await compileAqua(settings, textDocument, folders, connection.console);
const [diagnostics, locations, tokenInfos] = await compileAqua(settings, textDocument, folders, connection.console);
allLocations.set(textDocument.uri, locations);
allPageInfo.set(textDocument.uri, { links: locations, tokens: tokenInfos });
// Send the computed diagnostics to VSCode.
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });

View File

@ -5,7 +5,13 @@ import type { TextDocument } from 'vscode-languageserver-textdocument';
import { Diagnostic, DiagnosticSeverity, RemoteConsole } from 'vscode-languageserver/node';
import type { WorkspaceFolder } from 'vscode-languageserver-protocol';
import { AquaLSP, ErrorInfo, TokenLink, WarningInfo } from '@fluencelabs/aqua-language-server-api/aqua-lsp-api';
import {
AquaLSP,
ErrorInfo,
TokenInfo,
TokenLink,
WarningInfo,
} from '@fluencelabs/aqua-language-server-api/aqua-lsp-api';
import type { Settings } from './settings';
@ -141,7 +147,7 @@ export async function compileAqua(
textDocument: TextDocument,
folders: WorkspaceFolder[],
console: RemoteConsole,
): Promise<[Diagnostic[], TokenLink[]]> {
): Promise<[Diagnostic[], TokenLink[], TokenInfo[]]> {
const uri = textDocument.uri.replace('file://', '');
const imports = getImports(settings, textDocument, folders, console);
@ -184,5 +190,5 @@ export async function compileAqua(
const locations = result.locations.concat(links);
return [diagnostics, locations];
return [diagnostics, locations, result.tokens];
}