Files
newt/newt-vscode-lsp/src/lsp.ts

113 lines
3.5 KiB
TypeScript

/**
* 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,
Location,
} from "vscode-languageserver/node";
import { TextDocument } from "vscode-languageserver-textdocument";
const connection = createConnection(ProposedFeatures.all);
const documents = new TextDocuments(TextDocument);
// waiting for whomever to send accumulated changes
const DELAY_AFTER_SEND = 200
// waiting for typing to quiesce (and it seems accumulated changes have a delay between them)
const DELAY_AFTER_CHANGE = 100
// the last is the most important to the user, but we run FIFO
// to ensure dependencies are seen in causal order
let changes: TextDocument[] = []
let running: NodeJS.Timeout | undefined
let lastChange = 0
function addChange(doc: TextDocument) {
console.log('enqueue', doc.uri)
// drop stale pending changes
let before = changes.length
changes = changes.filter(ch => ch.uri != doc.uri)
console.log('DROP', before - changes.length);
changes.push(doc)
lastChange = +new Date()
// I'm not sure if this timeout is a big deal
if (!running) running = setTimeout(runChange, DELAY_AFTER_CHANGE)
}
function runChange() {
let now = +new Date()
if ( now - lastChange < DELAY_AFTER_CHANGE) running = setTimeout(runChange, DELAY_AFTER_CHANGE);
let doc = changes.shift()
if (!doc) {
running = undefined
return
}
const uri = doc.uri
const start = +new Date()
const diagnostics = LSP_checkFile(doc.uri)
const end = +new Date()
connection.sendDiagnostics({uri,diagnostics})
console.log('CHECK', doc.uri, 'in', end-start);
console.log("GOT\n",JSON.stringify(diagnostics, null, ' '))
running = undefined
// If we just sent one, it seems that we need to give vscode some time to send the rest
// Otherwise, for Elab.newt, we hit 1.8s for each character typed.
// 1 ms doesn't work, so I guess we don't have the changes accumulated locally.
running = setTimeout(runChange, DELAY_AFTER_SEND)
}
documents.onDidChangeContent(async (change) => {
console.log('DIDCHANGE', change.document.uri)
const uri = change.document.uri;
const text = change.document.getText();
LSP_updateFile(uri, text);
// we defer the check to let all of the changes file in.
addChange(change.document);
// const diagnostics = LSP_checkFile(uri);
// console.log(`Got ${JSON.stringify(diagnostics, null, ' ')}`)
// connection.sendDiagnostics({ uri, diagnostics })
});
connection.onHover((params): Hover | null => {
// wait until quiesced (REVIEW after query-based)
if (running) return null
const uri = params.textDocument.uri;
const pos = params.position;
console.log('HOVER', uri, pos)
let res = LSP_hoverInfo(uri, pos.line, pos.character)
if (!res) return null
console.log('HOVER is ', res)
return { contents: { kind: "plaintext", value: res.info } };
});
connection.onDefinition((params): Location | null => {
const uri = params.textDocument.uri;
const pos = params.position;
let value = LSP_hoverInfo(uri, pos.line, pos.character)
if (!value) return null;
return value.location
})
connection.onInitialize((_params: InitializeParams): InitializeResult => ({
capabilities: {
textDocumentSync: TextDocumentSyncKind.Incremental,
hoverProvider: true,
definitionProvider: true,
},
}));
documents.listen(connection);
connection.listen();
console.log('STARTED')