diff options
| author | Daniel Gavin <danielgavin5@hotmail.com> | 2021-12-25 13:43:58 +0100 |
|---|---|---|
| committer | Daniel Gavin <danielgavin5@hotmail.com> | 2021-12-25 13:43:58 +0100 |
| commit | bb1379a911f43134b8dc5ed9ec2eebb889b800fb (patch) | |
| tree | 8eb5090124d0bb4c18d353e1daf1d09d50531f8d /editors | |
| parent | 74d5978d86d8da1e2f74727965d7d3d3b16c71f1 (diff) | |
add temp work
Diffstat (limited to 'editors')
| -rw-r--r-- | editors/vscode/package.json | 31 | ||||
| -rw-r--r-- | editors/vscode/src/ctx.ts | 9 | ||||
| -rw-r--r-- | editors/vscode/src/extension.ts | 7 | ||||
| -rw-r--r-- | editors/vscode/src/inlay_hints.ts | 244 |
4 files changed, 287 insertions, 4 deletions
diff --git a/editors/vscode/package.json b/editors/vscode/package.json index cf2db7c..678f058 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -34,6 +34,11 @@ "command": "ols.restart", "title": "Restart Odin Language Server", "category": "Odin Language Server" + }, + { + "command": "ols.createOls", + "title": "Create ols.json file in project", + "category": "Odin Language Server" } ], "configuration": { @@ -118,7 +123,27 @@ "scopeName": "source.odin", "path": "./syntaxes/odin.tmLanguage.json" } - ] + ], + "colors": [ + { + "id": "odin.inlayHints.foreground", + "description": "Foreground color of inlay hints", + "defaults": { + "dark": "#A0A0A0F0", + "light": "#747474", + "highContrast": "#BEBEBE" + } + }, + { + "id": "odin.inlayHints.background", + "description": "Background color of inlay hints", + "defaults": { + "dark": "#11223300", + "light": "#11223300", + "highContrast": "#11223300" + } + } + ] }, "scripts": { "vscode:prepublish": "npm run compile", @@ -130,10 +155,10 @@ }, "devDependencies": { "@types/glob": "^7.1.3", - "@types/mocha": "^8.2.2", + "@types/mocha": "^9.0.0", "@types/node": "^14.14.43", "@types/node-fetch": "^2.5.7", - "@types/vscode": "^1.55.0", + "@types/vscode": "^1.60.0", "@typescript-eslint/eslint-plugin": "^4.22.1", "@typescript-eslint/parser": "^4.22.1", "eslint": "^7.25.0", diff --git a/editors/vscode/src/ctx.ts b/editors/vscode/src/ctx.ts index 7abf9c4..a1bf5d4 100644 --- a/editors/vscode/src/ctx.ts +++ b/editors/vscode/src/ctx.ts @@ -4,6 +4,8 @@ import * as lc from 'vscode-languageclient/node'; import { Config } from './config'; import { isOdinEditor, OdinEditor } from './util'; +import * as inlayHints from './inlay_hints'; + //modified from https://github.com/rust-analyzer/rust-analyzer/blob/master/editors/code/src/ctx.ts - 09.05.2021 export class Ctx { @@ -24,6 +26,9 @@ export class Ctx { cwd: string, ): Promise<Ctx> { const res = new Ctx(config, extCtx, client, serverPath); + + inlayHints.activate(res); + return res; } @@ -53,6 +58,10 @@ export class Ctx { return this.extCtx.subscriptions; } + isOdinDocument(document: vscode.TextDocument): number { + return vscode.languages.match({scheme: 'file', language: 'odin'}, document); + } + pushCleanup(d: Disposable) { this.extCtx.subscriptions.push(d); } diff --git a/editors/vscode/src/extension.ts b/editors/vscode/src/extension.ts index e7163c2..69dcd28 100644 --- a/editors/vscode/src/extension.ts +++ b/editors/vscode/src/extension.ts @@ -139,6 +139,10 @@ export async function activate(context: vscode.ExtensionContext) { client.start(); }); + vscode.commands.registerCommand("ols.createOls", async() => { + createOlsConfig(ctx); + }); + client.start(); parseOlsFile(config, olsFile); @@ -183,7 +187,8 @@ export function createOlsConfig(ctx: Ctx) { collections: [{ name: "core", path: corePath }], enable_document_symbols: true, enable_semantic_tokens: false, - enable_hover: true + enable_hover: true, + enable_snippets: true }; const olsPath = vscode.workspace.workspaceFolders![0].uri.fsPath; diff --git a/editors/vscode/src/inlay_hints.ts b/editors/vscode/src/inlay_hints.ts new file mode 100644 index 0000000..441cd0c --- /dev/null +++ b/editors/vscode/src/inlay_hints.ts @@ -0,0 +1,244 @@ +// modification of https://github.com/clangd/vscode-clangd/blob/master/src/inlay-hints.ts + +import * as vscode from 'vscode'; +import * as vscodelc from 'vscode-languageclient/node'; + +import { Ctx } from './ctx'; + +export function activate(context: Ctx) { + const feature = new InlayHintsFeature(context); + context.client.registerFeature(feature); +} + +// Currently, only one hint kind (parameter hints) are supported, +// but others (e.g. type hints) may be added in the future. +enum InlayHintKind { + Parameter = 'parameter', + Type = 'type' +} + +interface InlayHint { + range: vscodelc.Range; + kind: InlayHintKind | string; + label: string; +} + +interface InlayHintsParams { + textDocument: vscodelc.TextDocumentIdentifier; +} + +namespace InlayHintsRequest { + export const type = + new vscodelc.RequestType<InlayHintsParams, InlayHint[], void>( + 'odin/inlayHints'); +} + +interface InlayDecorations { + // Hints are grouped based on their InlayHintKind, because different kinds + // require different decoration types. + // A future iteration of the API may have free-form hint kinds, and instead + // specify style-related information (e.g. before vs. after) explicitly. + // With such an API, we could group hints based on unique presentation styles + // instead. + parameterHints: vscode.DecorationOptions[]; + typeHints: vscode.DecorationOptions[]; +} + +interface HintStyle { + decorationType: vscode.TextEditorDecorationType; + + toDecoration(hint: InlayHint, + conv: vscodelc.Protocol2CodeConverter): vscode.DecorationOptions; +} + +const parameterHintStyle = createHintStyle('before'); +const typeHintStyle = createHintStyle('after'); + +function createHintStyle(position: 'before' | 'after'): HintStyle { + const fg = new vscode.ThemeColor('odin.inlayHints.foreground'); + const bg = new vscode.ThemeColor('odin.inlayHints.background'); + return { + decorationType: vscode.window.createTextEditorDecorationType({ + [position]: { + color: fg, + backgroundColor: bg, + fontStyle: 'normal', + fontWeight: 'normal', + textDecoration: ';font-size:smaller' + } + }), + toDecoration(hint: InlayHint, conv: vscodelc.Protocol2CodeConverter): + vscode.DecorationOptions { + return { + range: conv.asRange(hint.range), + renderOptions: { [position]: { contentText: hint.label } } + }; + } + }; +} + +interface FileEntry { + document: vscode.TextDocument; + + // Last applied decorations. + cachedDecorations: InlayDecorations | null; + + // Source of the token to cancel in-flight inlay hints request if any. + inlaysRequest: vscode.CancellationTokenSource | null; +} + +class InlayHintsFeature implements vscodelc.StaticFeature { + private enabled = false; + private sourceFiles = new Map<string, FileEntry>(); // keys are URIs + private readonly disposables: vscode.Disposable[] = []; + + constructor(private readonly context: Ctx) { } + + fillClientCapabilities(_capabilities: vscodelc.ClientCapabilities) { } + fillInitializeParams(_params: vscodelc.InitializeParams) { } + + initialize(capabilities: vscodelc.ServerCapabilities, + _documentSelector: vscodelc.DocumentSelector | undefined) { + const serverCapabilities: vscodelc.ServerCapabilities & + { inlayHintsProvider?: boolean } = capabilities; + if (serverCapabilities.inlayHintsProvider) { + this.enabled = true; + this.startShowingHints(); + } + } + + onDidChangeVisibleTextEditors() { + if (!this.enabled) { + return; + } + + + const newSourceFiles = new Map<string, FileEntry>(); + + // Rerender all, even up-to-date editors for simplicity + this.context.visibleOdinEditors.forEach(async editor => { + const uri = editor.document.uri.toString(); + const file = this.sourceFiles.get(uri) ?? { + document: editor.document, + cachedDecorations: null, + inlaysRequest: null + }; + newSourceFiles.set(uri, file); + + // No text documents changed, so we may try to use the cache + if (!file.cachedDecorations) { + const hints = await this.fetchHints(file); + if (!hints) { + return; + } + + file.cachedDecorations = this.hintsToDecorations(hints); + } + + this.renderDecorations(editor, file.cachedDecorations); + }); + + // Cancel requests for no longer visible (disposed) source files + this.sourceFiles.forEach((file, uri) => { + if (!newSourceFiles.has(uri)) { + file.inlaysRequest?.cancel(); + } + }); + + this.sourceFiles = newSourceFiles; + } + + onDidChangeTextDocument({ contentChanges, + document }: vscode.TextDocumentChangeEvent) { + if (!this.enabled || contentChanges.length === 0 || + !this.context.isOdinDocument(document)) { + return; + } + + this.syncCacheAndRenderHints(); + } + + dispose() { this.stopShowingHints(); } + + private startShowingHints() { + vscode.window.onDidChangeVisibleTextEditors( + this.onDidChangeVisibleTextEditors, this, this.disposables); + vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, + this.disposables); + + // Set up initial cache shape + this.context.visibleOdinEditors.forEach( + editor => this.sourceFiles.set(editor.document.uri.toString(), { + document: editor.document, + inlaysRequest: null, + cachedDecorations: null + })); + + this.syncCacheAndRenderHints(); + } + + private stopShowingHints() { + this.sourceFiles.forEach(file => file.inlaysRequest?.cancel()); + this.context.visibleOdinEditors.forEach( + editor => this.renderDecorations(editor, + { parameterHints: [], typeHints: [] })); + this.disposables.forEach(d => d.dispose()); + } + + private renderDecorations(editor: vscode.TextEditor, + decorations: InlayDecorations) { + editor.setDecorations(parameterHintStyle.decorationType, + decorations.parameterHints); + editor.setDecorations(typeHintStyle.decorationType, decorations.typeHints); + } + + private syncCacheAndRenderHints() { + this.sourceFiles.forEach( + (file, uri) => this.fetchHints(file).then(hints => { + if (!hints) { + return; + } + + file.cachedDecorations = this.hintsToDecorations(hints); + + for (const editor of this.context.visibleOdinEditors) { + if (editor.document.uri.toString() == uri) { + this.renderDecorations(editor, file.cachedDecorations); + } + } + })); + } + + private hintsToDecorations(hints: InlayHint[]): InlayDecorations { + const decorations: InlayDecorations = { parameterHints: [], typeHints: [] }; + const conv = this.context.client.protocol2CodeConverter; + for (const hint of hints) { + switch (hint.kind) { + case InlayHintKind.Parameter: { + decorations.parameterHints.push( + parameterHintStyle.toDecoration(hint, conv)); + continue; + } + case InlayHintKind.Type: { + decorations.typeHints.push(typeHintStyle.toDecoration(hint, conv)); + continue; + } + // Don't handle unknown hint kinds because we don't know how to style + // them. This may change in a future version of the protocol. + } + } + return decorations; + } + + private async fetchHints(file: FileEntry): Promise<InlayHint[] | null> { + file.inlaysRequest?.cancel(); + + const tokenSource = new vscode.CancellationTokenSource(); + file.inlaysRequest = tokenSource; + + const request = { textDocument: { uri: file.document.uri.toString() } }; + + return this.context.client.sendRequest(InlayHintsRequest.type, request, + tokenSource.token); + } +}
\ No newline at end of file |