Skip to content

Commit

Permalink
Added go-to-defintion feature to weidu .d files
Browse files Browse the repository at this point in the history
  • Loading branch information
podcherklife committed Jul 31, 2024
1 parent 96f48ca commit 5b3f6a1
Show file tree
Hide file tree
Showing 12 changed files with 9,501 additions and 5 deletions.
175 changes: 175 additions & 0 deletions server/src/d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { CharStreams, CommonTokenStream, ParserRuleContext } from "antlr4ts";
import { DLexer } from "./dparser/DLexer";
import { AppendDActionContext, BeginDActionContext, DParser, ExternTransitionTargetContext, GotoTransitionTargetContext, IfThenStateContext, ReplaceDActionContext, StringRuleContext } from "./dparser/DParser";

Check warning on line 3 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

'StringRuleContext' is defined but never used
import { DParserVisitor } from "./dparser/DParserVisitor";
import { AbstractParseTreeVisitor, ErrorNode, ParseTree, RuleNode } from "antlr4ts/tree"

Check warning on line 5 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

'ParseTree' is defined but never used
import { HeaderData } from "./language";
import { Location, Position } from "vscode-languageserver";


export function loadFileData(uri: string, str: string): HeaderData {
const definedStates = collectStatesFromFile(str);
const definitions: [string, Location][] = definedStates.map(s => [`${s.dlg}@${s.label}`, {
range: {
start: {
line: s.line,
character: s.start
},
end: {
line: s.line,
character: s.end
},
},
uri: uri
}])
return {
definition: new Map(definitions),
hover: new Map(),
completion: []
}
}

function collectStatesFromFile(str: string) {
const parseTree = parseSilently(str);
const statesCollector = new StateDefinitionCollector();
const collectedStates = parseTree.accept(statesCollector);
return collectedStates;
}

export function findSymbolAtPosition(str: string, pos: Position): string {
const allSymbols = parseSilently(str).accept(new SymbolCollector())
const foundSymbol = allSymbols.find(s => {
return s.location.range.start.line <= pos.line && s.location.range.end.line >= pos.line
&& s.location.range.start.character <= pos.character && s.location.range.end.character >= pos.character
})
return foundSymbol?.id || ""
}

function parseSilently(str: string) {
const charStream = CharStreams.fromString(str);
const lexer = new DLexer(charStream);
const tokenStream = new CommonTokenStream(lexer);
const parser = new DParser(tokenStream);
parser.removeErrorListeners();
return parser.dFileRule();
}

function cleanString(str: string) {
if (str.startsWith('~~~~~')) {
return str.substring(5, str.length - 5);
} else if (str.length > 0 && str[0] == '~' || str[0] == "\"" || str[0] == '%') {
return str.substring(1, str.length - 1)
} else {
return str
}
}


abstract class DlgAwareErroResistantVisitor<T> extends AbstractParseTreeVisitor<T> implements DParserVisitor<T> {
dlgStack: string[] = [];

protected visitChildrenWithUpdatedDlg(dlg: string, ctx: RuleNode): T {
this.dlgStack.push(dlg);
const res = this.visitChildren(ctx);
this.dlgStack.pop();
return res;
}

get currentDlg() {
return this.dlgStack.at(-1)
}

visitChildren(node: RuleNode): T {
for (let i = node.childCount; i < node.childCount; i++) {
if (node instanceof ErrorNode) {
return this.defaultResult();
}
}
return super.visitChildren(node);
}

visitBeginDAction?: ((ctx: BeginDActionContext) => T) = ctx => this.visitChildrenWithUpdatedDlg(cleanString(ctx._dlg.text), ctx);

visitAppendDAction?: ((ctx: AppendDActionContext) => T) = ctx => this.visitChildrenWithUpdatedDlg(cleanString(ctx._dlg.text), ctx);

visitReplaceDAction?: ((ctx: ReplaceDActionContext) => T) = ctx => this.visitChildrenWithUpdatedDlg(cleanString(ctx._dlg.text), ctx);
}


interface Symbol {
id: string,
location: Omit<Location, 'uri'>
}


class SymbolCollector extends DlgAwareErroResistantVisitor<Symbol[]> {

Check failure on line 105 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Don't use `Symbol` as a type. Use symbol instead
dlgStack: string[] = [];

protected defaultResult(): Symbol[] {

Check failure on line 108 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Don't use `Symbol` as a type. Use symbol instead
return [];
}

protected aggregateResult(aggregate: Symbol[], nextResult: Symbol[]): Symbol[] {

Check failure on line 112 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Don't use `Symbol` as a type. Use symbol instead

Check failure on line 112 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Don't use `Symbol` as a type. Use symbol instead

Check failure on line 112 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Don't use `Symbol` as a type. Use symbol instead
return aggregate.concat(nextResult)
}


visitExternTransitionTarget?: ((ctx: ExternTransitionTargetContext) => Symbol[]) = ctx => {

Check failure on line 117 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Don't use `Symbol` as a type. Use symbol instead
return [{
id: `${cleanString(ctx._dlg.text)}@${cleanString(ctx._label.text)}`,
location: makePartialLocationFromParseTree(ctx)
}]
}

visitGotoTransitionTarget?: ((ctx: GotoTransitionTargetContext) => Symbol[]) = ctx => {

Check failure on line 124 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Don't use `Symbol` as a type. Use symbol instead
return [{
id: `${this.currentDlg!}@${cleanString(ctx._label.text)}`,

Check warning on line 126 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion
location: makePartialLocationFromParseTree(ctx)
}]
}
}

interface LocalStateDefinition {
dlg: string
label: string
line: number
start: number
end: number
}

class StateDefinitionCollector extends DlgAwareErroResistantVisitor<LocalStateDefinition[]> {

protected defaultResult(): LocalStateDefinition[] {
return [];
}

protected aggregateResult(aggregate: LocalStateDefinition[], nextResult: LocalStateDefinition[]): LocalStateDefinition[] {
return aggregate.concat(nextResult);
}

visitIfThenState?: ((ctx: IfThenStateContext) => LocalStateDefinition[]) = ctx => {
return [{
dlg: this.currentDlg!,

Check warning on line 152 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion
label: cleanString(ctx._label.text),
start: ctx._label.start.charPositionInLine,
end: ctx._label.start.charPositionInLine + ctx._label.text.length,
line: ctx._label.start.line - 1,
}]
}
}

function makePartialLocationFromParseTree(ctx: ParserRuleContext) {
return {
range: {
start: {
line: ctx.start.line - 1,
character: ctx.start.charPositionInLine
},
end: {
line: ctx.stop!.line - 1,

Check warning on line 169 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion
character: ctx.stop!.charPositionInLine + ctx.stop!.text!.length

Check warning on line 170 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion

Check warning on line 170 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion

Check warning on line 170 in server/src/d.ts

View workflow job for this annotation

GitHub Actions / build

Forbidden non-null assertion
}
}
}
}

Loading

0 comments on commit 5b3f6a1

Please sign in to comment.