Skip to content

Commit

Permalink
Fixes peggyjs#46
Browse files Browse the repository at this point in the history
Wait for parsing to finish before updating outline.
Depends on peggyjs#53.
  • Loading branch information
hildjj committed Oct 13, 2024
1 parent 0ab3b20 commit 954651c
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 152 deletions.
316 changes: 164 additions & 152 deletions server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from "vscode-languageserver/node";
import { Position, TextDocument } from "vscode-languageserver-textdocument";
import type { SourceNode } from "source-map-generator";
import { WatchMap } from "./watchMap";
import { debounce } from "../common/debounce";

function getSession(
Expand All @@ -35,10 +36,7 @@ function getSession(
ast.code = session as unknown as SourceNode;
}

interface AstCache {
[uri: string]: any;
}
const AST: AstCache = {};
const AST = new WatchMap<string, any>();
const WORD_RE = /[^\s{}[\]()`~!@#%^&*+\-=|\\;:'",./<>?]+/g;
const PASSES: peggy.compiler.Stages = {
prepare: peggy.compiler.passes.prepare,
Expand Down Expand Up @@ -158,7 +156,7 @@ const validateTextDocument = debounce((doc: TextDocument): void => {
{ grammarSource: doc.uri, output: "source-and-map" }
) as unknown as peggy.Session;
addProblemDiagnostics(session.problems, diagnostics);
AST[doc.uri] = ast;
AST.set(doc.uri, ast);
} catch (error) {
if (error instanceof peggy.GrammarError) {
addProblemDiagnostics(error.problems, diagnostics);
Expand Down Expand Up @@ -219,172 +217,186 @@ function ruleNameRange(name: string, ruleRange: Range): Range {
};
}

connection.onCompletion((pos: TextDocumentPositionParams): CompletionItem[] => {
const docAST = AST[pos.textDocument.uri];
if (!docAST || (docAST.rules.length === 0)) {
return null;
}
const document = documents.get(pos.textDocument.uri);
if (!document) {
return null;
}
const word = getWordAtPosition(document, pos.position);
if (word === "") {
return null;
}

return docAST.rules.filter(
(r: any) => r.name.startsWith(word)
).map((r: any) => ({
label: r.name,
}));
});
connection.onCompletion(
async(pos: TextDocumentPositionParams): Promise<CompletionItem[]> => {
const docAST = await AST.waitFor(pos.textDocument.uri);
if (!docAST || (docAST.rules.length === 0)) {
return null;
}
const document = documents.get(pos.textDocument.uri);
if (!document) {
return null;
}
const word = getWordAtPosition(document, pos.position);
if (word === "") {
return null;
}

connection.onDefinition((pos: TextDocumentPositionParams): LocationLink[] => {
const docAST = AST[pos.textDocument.uri];
if (!docAST || (docAST.rules.length === 0)) {
return null;
}
const document = documents.get(pos.textDocument.uri);
if (!document) {
return null;
}
const word = getWordAtPosition(document, pos.position);
if (word === "") {
return null;
return docAST.rules.filter(
(r: any) => r.name.startsWith(word)
).map((r: any) => ({
label: r.name,
}));
}
);

const rule = docAST.rules.find((r: any) => r.name === word);
if (!rule) {
return null;
}
const targetRange = peggyLoc_to_vscodeRange(rule.location);
const targetSelectionRange = ruleNameRange(rule.name, targetRange);

return [
{
targetUri: pos.textDocument.uri,
targetRange,
targetSelectionRange,
},
];
});
connection.onDefinition(
async(pos: TextDocumentPositionParams): Promise<LocationLink[]> => {
const docAST = await AST.waitFor(pos.textDocument.uri);
if (!docAST || (docAST.rules.length === 0)) {
return null;
}
const document = documents.get(pos.textDocument.uri);
if (!document) {
return null;
}
const word = getWordAtPosition(document, pos.position);
if (word === "") {
return null;
}

connection.onReferences((pos: TextDocumentPositionParams): Location[] => {
const docAST = AST[pos.textDocument.uri];
if (!docAST || (docAST.rules.length === 0)) {
return null;
}
const document = documents.get(pos.textDocument.uri);
if (!document) {
return null;
}
const word = getWordAtPosition(document, pos.position);
if (word === "") {
return null;
const rule = docAST.rules.find((r: any) => r.name === word);
if (!rule) {
return null;
}
const targetRange = peggyLoc_to_vscodeRange(rule.location);
const targetSelectionRange = ruleNameRange(rule.name, targetRange);

return [
{
targetUri: pos.textDocument.uri,
targetRange,
targetSelectionRange,
},
];
}
const results: Location[] = [];
const visit = peggy.compiler.visitor.build({
rule_ref(node: any): void {
if (node.name !== word) { return; }
results.push({
uri: pos.textDocument.uri,
range: peggyLoc_to_vscodeRange(node.location),
});
},
});
visit(docAST);
);

return results;
});
connection.onReferences(
async(pos: TextDocumentPositionParams): Promise<Location[]> => {
const docAST = await AST.get(pos.textDocument.uri);
if (!docAST || (docAST.rules.length === 0)) {
return null;
}
const document = documents.get(pos.textDocument.uri);
if (!document) {
return null;
}
const word = getWordAtPosition(document, pos.position);
if (word === "") {
return null;
}
const results: Location[] = [];
const visit = peggy.compiler.visitor.build({
rule_ref(node: any): void {
if (node.name !== word) { return; }
results.push({
uri: pos.textDocument.uri,
range: peggyLoc_to_vscodeRange(node.location),
});
},
});
visit(docAST);

connection.onRenameRequest((pos: RenameParams): WorkspaceEdit => {
const docAST = AST[pos.textDocument.uri];
if (!docAST || (docAST.rules.length === 0)) {
return null;
}
const document = documents.get(pos.textDocument.uri);
if (!document) {
return null;
}
const word = getWordAtPosition(document, pos.position);
if (word === "") {
return null;
return results;
}
);

const edits: TextEdit[] = [];
const visit = peggy.compiler.visitor.build({
rule_ref(node: any): void {
if (node.name !== word) { return; }
edits.push({
newText: pos.newName,
range: peggyLoc_to_vscodeRange(node.location),
});
},

rule(node: any): void {
visit(node.expression);
if (node.name !== word) { return; }
edits.push({
newText: pos.newName,
range: ruleNameRange(node.name, peggyLoc_to_vscodeRange(node.location)),
});
},
});
visit(docAST);
connection.onRenameRequest(
async(pos: RenameParams): Promise<WorkspaceEdit> => {
const docAST = await AST.get(pos.textDocument.uri);
if (!docAST || (docAST.rules.length === 0)) {
return null;
}
const document = documents.get(pos.textDocument.uri);
if (!document) {
return null;
}
const word = getWordAtPosition(document, pos.position);
if (word === "") {
return null;
}

return {
changes: {
[pos.textDocument.uri]: edits,
},
};
});
const edits: TextEdit[] = [];
const visit = peggy.compiler.visitor.build({
rule_ref(node: any): void {
if (node.name !== word) { return; }
edits.push({
newText: pos.newName,
range: peggyLoc_to_vscodeRange(node.location),
});
},

rule(node: any): void {
visit(node.expression);
if (node.name !== word) { return; }
edits.push({
newText: pos.newName,
range: ruleNameRange(
node.name,
peggyLoc_to_vscodeRange(node.location)
),
});
},
});
visit(docAST);

connection.onDocumentSymbol((pos: DocumentSymbolParams): DocumentSymbol[] => {
const docAST = AST[pos.textDocument.uri];
if (!docAST) {
return null;
return {
changes: {
[pos.textDocument.uri]: edits,
},
};
}
);

const symbols = docAST.rules.map((r: any) => {
const range = peggyLoc_to_vscodeRange(r.location);
const ret: DocumentSymbol = {
name: r.name,
kind: SymbolKind.Function,
range,
selectionRange: ruleNameRange(r.name, range),
};
if (r.expression.type === "named") {
ret.detail = r.expression.name;
connection.onDocumentSymbol(
async(pos: DocumentSymbolParams): Promise<DocumentSymbol[]> => {
const docAST = await AST.waitFor(pos.textDocument.uri);
if (!docAST) {
return null;
}

return ret;
});
if (docAST.initializer) {
const range = peggyLoc_to_vscodeRange(docAST.initializer.location);
symbols.unshift({
name: "{Per-parse initializer}",
kind: SymbolKind.Constructor,
range,
selectionRange: ruleNameRange("{", range),
});
}
if (docAST.topLevelInitializer) {
const range = peggyLoc_to_vscodeRange(docAST.topLevelInitializer.location);
symbols.unshift({
name: "{{Global initializer}}",
kind: SymbolKind.Constructor,
range,
selectionRange: ruleNameRange("{{", range),
const symbols = docAST.rules.map((r: any) => {
const range = peggyLoc_to_vscodeRange(r.location);
const ret: DocumentSymbol = {
name: r.name,
kind: SymbolKind.Function,
range,
selectionRange: ruleNameRange(r.name, range),
};
if (r.expression.type === "named") {
ret.detail = r.expression.name;
}

return ret;
});
}
if (docAST.initializer) {
const range = peggyLoc_to_vscodeRange(docAST.initializer.location);
symbols.unshift({
name: "{Per-parse initializer}",
kind: SymbolKind.Constructor,
range,
selectionRange: ruleNameRange("{", range),
});
}
if (docAST.topLevelInitializer) {
const range = peggyLoc_to_vscodeRange(
docAST.topLevelInitializer.location
);
symbols.unshift({
name: "{{Global initializer}}",
kind: SymbolKind.Constructor,
range,
selectionRange: ruleNameRange("{{", range),
});
}

return symbols;
});
return symbols;
}
);

documents.onDidClose(change => {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete AST[change.document.uri.toString()];
AST.delete(change.document.uri.toString());
});

documents.onDidChangeContent(change => {
Expand Down
Loading

0 comments on commit 954651c

Please sign in to comment.