Initial LSP implementation/vscode support
This commit is contained in:
36
newt-vscode-lsp/src/abbrev.ts
Normal file
36
newt-vscode-lsp/src/abbrev.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
// This needs to be fleshed out a lot, I've been adding them as needed
|
||||
// There is a mix of agda, lean, and my own shortcuts here
|
||||
export const ABBREV: Record<string, string> = {
|
||||
"\\x": "×",
|
||||
"\\r": "→",
|
||||
"\\all": "∀",
|
||||
"\\\\": "\\",
|
||||
"\\==": "≡",
|
||||
"\\circ": "∘",
|
||||
"\\oplus": "⊕",
|
||||
"\\otimes": "⊗",
|
||||
// lean
|
||||
"\\1": "₁",
|
||||
"\\2": "₂",
|
||||
"\\<": "⟨",
|
||||
"\\>": "⟩",
|
||||
// agda
|
||||
"\\_0": "₀",
|
||||
"\\_1": "₁",
|
||||
"\\_2": "₂",
|
||||
"\\_3": "₃",
|
||||
// lean has \n here, which is a royal pain
|
||||
"\\neg": "¬",
|
||||
"\\bN": "ℕ",
|
||||
"\\bZ": "ℤ",
|
||||
"\\GG": "Γ",
|
||||
"\\Gi": "ι",
|
||||
"\\Gl": "λ",
|
||||
"\\Gs": "σ",
|
||||
"\\Gt": "τ",
|
||||
"\\GD": "Δ",
|
||||
"\\GS": "Σ",
|
||||
"\\GP": "∏",
|
||||
"\\[[": "⟦",
|
||||
"\\]]": "⟧",
|
||||
};
|
||||
95
newt-vscode-lsp/src/extension.ts
Normal file
95
newt-vscode-lsp/src/extension.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import * as vscode from "vscode";
|
||||
import { ABBREV } from "./abbrev";
|
||||
import {
|
||||
LanguageClient,
|
||||
LanguageClientOptions,
|
||||
ServerOptions,
|
||||
TransportKind
|
||||
} from 'vscode-languageclient/node';
|
||||
|
||||
// They put this at module level for deactivate below.
|
||||
let client: LanguageClient
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
const serverModule = context.asAbsolutePath('./dist/lsp.js')
|
||||
console.log('*** servervModule', serverModule)
|
||||
const config = vscode.workspace.getConfiguration("newt");
|
||||
const cmd = config.get<string | null>("lspPath");
|
||||
|
||||
let serverOptions: ServerOptions
|
||||
if (cmd) {
|
||||
serverOptions = {
|
||||
run: { command: "node", args: [cmd], transport: TransportKind.pipe },
|
||||
debug: { command: "node", args: [cmd], transport: TransportKind.pipe },
|
||||
}
|
||||
} else {
|
||||
serverOptions = {
|
||||
run: { module: serverModule, transport: TransportKind.ipc },
|
||||
debug: { module: serverModule, transport: TransportKind.ipc },
|
||||
}
|
||||
}
|
||||
|
||||
const clientOptions: LanguageClientOptions = {
|
||||
documentSelector: [{ scheme: 'file', language: 'newt' }],
|
||||
synchronize: {
|
||||
fileEvents: vscode.workspace.createFileSystemWatcher('*.newt')
|
||||
}
|
||||
}
|
||||
|
||||
client = new LanguageClient(
|
||||
'NewtLanguageServer',
|
||||
'Newt Language Server',
|
||||
serverOptions,
|
||||
clientOptions
|
||||
)
|
||||
|
||||
client.start();
|
||||
console.log('CLIENT started')
|
||||
|
||||
vscode.workspace.onDidChangeTextDocument((event) => {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor || event.document !== editor.document) return;
|
||||
|
||||
const changes = event.contentChanges;
|
||||
if (changes.length === 0) return;
|
||||
|
||||
// TODO - agda input mode does the replacement as soon as possible
|
||||
// but if the sequence is a prefix, it will change for subsequent characters
|
||||
// The latter would require keeping state, but if we don't allow sequences to prefix
|
||||
// each other, we could activate quicker.
|
||||
const lastChange = changes[changes.length - 1];
|
||||
const text = lastChange.text;
|
||||
// Check if the last change is a potential shortcut trigger
|
||||
if (!text || !(" ')\\_".includes(text) || text.startsWith('\n'))) return;
|
||||
|
||||
const document = editor.document;
|
||||
const position = lastChange.range.end;
|
||||
const lineText = document.lineAt(position.line).text;
|
||||
const start = Math.max(0, position.character - 10);
|
||||
const snippet = lineText.slice(start, position.character);
|
||||
const m = snippet.match(/(\\[^ ]+)$/);
|
||||
if (m) {
|
||||
const cand = m[0];
|
||||
console.log("cand", cand);
|
||||
const replacement = ABBREV[cand];
|
||||
console.log("repl", replacement);
|
||||
if (replacement) {
|
||||
const range = new vscode.Range(
|
||||
position.line,
|
||||
position.character - cand.length,
|
||||
position.line,
|
||||
position.character
|
||||
);
|
||||
editor.edit((editBuilder) => {
|
||||
editBuilder.replace(range, replacement);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
export function deactivate() {
|
||||
if (client) client.stop();
|
||||
}
|
||||
53
newt-vscode-lsp/src/lsp.ts
Normal file
53
newt-vscode-lsp/src/lsp.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* WIP
|
||||
*
|
||||
* Wraps newt.js (compiled from src/LSP.newt with some tweaks to `export`) with the
|
||||
* vscode LSP server module.
|
||||
*/
|
||||
|
||||
import { LSP_checkFile, LSP_updateFile, LSP_hoverInfo } from './newt.js'
|
||||
|
||||
import {
|
||||
createConnection,
|
||||
TextDocuments,
|
||||
ProposedFeatures,
|
||||
Hover,
|
||||
InitializeParams,
|
||||
InitializeResult,
|
||||
TextDocumentSyncKind,
|
||||
} from "vscode-languageserver/node";
|
||||
import { TextDocument } from "vscode-languageserver-textdocument";
|
||||
|
||||
const connection = createConnection(ProposedFeatures.all);
|
||||
const documents = new TextDocuments(TextDocument);
|
||||
|
||||
documents.onDidChangeContent(async (change) => {
|
||||
console.log('DIDCHANGE', change.document.uri)
|
||||
const uri = change.document.uri;
|
||||
const text = change.document.getText();
|
||||
LSP_updateFile(uri, text);
|
||||
const diagnostics = LSP_checkFile(uri);
|
||||
console.log(`Got ${JSON.stringify(diagnostics, null, ' ')}`)
|
||||
connection.sendDiagnostics({ uri, diagnostics })
|
||||
});
|
||||
|
||||
connection.onHover((params): Hover | null => {
|
||||
const uri = params.textDocument.uri;
|
||||
const pos = params.position;
|
||||
console.log('HOVER', uri, pos)
|
||||
let value = LSP_hoverInfo(uri, pos.line, pos.character)
|
||||
if (!value) return null
|
||||
console.log('HOVER is ', value)
|
||||
return { contents: { kind: "plaintext", value } };
|
||||
});
|
||||
|
||||
connection.onInitialize((_params: InitializeParams): InitializeResult => ({
|
||||
capabilities: {
|
||||
textDocumentSync: TextDocumentSyncKind.Incremental,
|
||||
hoverProvider: true,
|
||||
},
|
||||
}));
|
||||
|
||||
documents.listen(connection);
|
||||
connection.listen();
|
||||
console.log('STARTED')
|
||||
5
newt-vscode-lsp/src/newt.d.ts
vendored
Normal file
5
newt-vscode-lsp/src/newt.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Diagnostic } from "vscode-languageserver";
|
||||
|
||||
export function LSP_updateFile(name: string, content: string): (eta: any) => any;
|
||||
export function LSP_checkFile(name: string): Diagnostic[];
|
||||
export function LSP_hoverInfo(name: string, row: number, col: number): string|null;
|
||||
Reference in New Issue
Block a user