Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added go-to-definition feature to weidu .d files #74

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading