refactor: Go to defination
This commit is contained in:
parent
95ccbd604d
commit
56db02cc7d
|
@ -264,6 +264,10 @@ connection.onDefinition((params: DefinitionParams) => {
|
|||
const uri: string = params.textDocument.uri;
|
||||
const tree = getFileContext(uri);
|
||||
const definationListener = new DefinationListener(uri, topScope);
|
||||
// clear refToDef and topScope first
|
||||
refToDef.clear();
|
||||
topScope.clear();
|
||||
|
||||
antlr4.tree.ParseTreeWalker.DEFAULT.walk(definationListener, tree);
|
||||
|
||||
const document = documents.get(uri);
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
import Token from "../parser/antlr4/Token";
|
||||
import CMakeListener from "../parser/CMakeListener";
|
||||
import CMakeParser from "../parser/CMakeParser";
|
||||
import { DefinationListener, refToDef } from "./goToDefination";
|
||||
import { FileScope, FunctionScope, MacroScope, Scope } from "./scope";
|
||||
import { Sym, Type } from "./symbol";
|
||||
import { getFileContext, getIncludeFileUri, getSubCMakeListsUri } from "../utils";
|
||||
import antlr4 from "../parser/antlr4";
|
||||
import ParseTree from "../parser/antlr4/tree/ParseTree";
|
||||
|
||||
export class FuncMacroListener extends CMakeListener {
|
||||
private currentScope: Scope;
|
||||
private funcMacroSym: Sym;
|
||||
private inBody: boolean = false;
|
||||
|
||||
private parseTreeProperty = new Map<ParseTree, boolean>();
|
||||
|
||||
constructor(parent: Scope, symbol: Sym) {
|
||||
super();
|
||||
|
||||
if (symbol.getType() === Type.Function) {
|
||||
this.currentScope = new FunctionScope(parent);
|
||||
} else {
|
||||
this.currentScope = new MacroScope(parent);
|
||||
}
|
||||
|
||||
this.funcMacroSym = symbol;
|
||||
}
|
||||
|
||||
enterFunctionCmd(ctx: any): void {
|
||||
const funcToken: Token = ctx.argument(0).start;
|
||||
// token.line start from 1
|
||||
if (funcToken.line - 1 === this.funcMacroSym.getLine() &&
|
||||
funcToken.column === this.funcMacroSym.getColumn()) {
|
||||
// we now enter the desired function
|
||||
this.inBody = true;
|
||||
}
|
||||
}
|
||||
|
||||
exitEndFunctionCmd(ctx: any): void {
|
||||
this.inBody = false;
|
||||
}
|
||||
|
||||
enterMacroCmd(ctx: any): void {
|
||||
this.enterFunctionCmd(ctx);
|
||||
}
|
||||
|
||||
exitEndMacroCmd(ctx: any): void {
|
||||
this.exitEndFunctionCmd(ctx);
|
||||
}
|
||||
|
||||
enterSetCmd(ctx: any): void {
|
||||
if (!this.inBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create a variable symbol
|
||||
const varToken: Token = ctx.argument(0).start;
|
||||
const varSymbol: Sym = new Sym(varToken.text, Type.Variable,
|
||||
this.funcMacroSym.getUri(), varToken.line - 1, varToken.column);
|
||||
|
||||
if (this.funcMacroSym.getType() === Type.Function) {
|
||||
// define variable in function, add variable to function scope
|
||||
this.currentScope.define(varSymbol);
|
||||
} else {
|
||||
// define variable in macro, add to parent scope
|
||||
this.currentScope.getEnclosingScope().define(varSymbol);
|
||||
}
|
||||
}
|
||||
|
||||
enterOptionCmd(ctx: any): void {
|
||||
this.enterSetCmd(ctx);
|
||||
}
|
||||
|
||||
enterIncludeCmd(ctx: any): void {
|
||||
if (!this.inBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nameToken = ctx.argument(0).start;
|
||||
const fileUri: string = getIncludeFileUri(this.funcMacroSym.getUri(), nameToken.text);
|
||||
if (!fileUri) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add included module to refDef
|
||||
const refPos: string = this.funcMacroSym.getUri() + '_' + (nameToken.line - 1) + '_' +
|
||||
nameToken.column + '_' + nameToken.text;
|
||||
refToDef.set(refPos, {
|
||||
uri: fileUri,
|
||||
range: {
|
||||
start: {
|
||||
line: 0,
|
||||
character: 0
|
||||
},
|
||||
end: {
|
||||
line: Number.MAX_VALUE,
|
||||
character: Number.MAX_VALUE
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const tree = getFileContext(fileUri);
|
||||
const definationListener = new DefinationListener(fileUri, this.currentScope);
|
||||
antlr4.tree.ParseTreeWalker.DEFAULT.walk(definationListener, tree);
|
||||
}
|
||||
|
||||
enterAddSubDirCmd(ctx: any): void {
|
||||
this.parseTreeProperty.set(ctx, false);
|
||||
if (!this.inBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dirToken: Token = ctx.argument(0).start;
|
||||
const fileUri: string = getSubCMakeListsUri(this.funcMacroSym.getUri(), dirToken.text);
|
||||
if (!fileUri) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.parseTreeProperty.set(ctx, true);
|
||||
|
||||
// add subdir CMakeLists.txt to refDef
|
||||
const refPos: string = this.funcMacroSym.getUri() + '_' + (dirToken.line - 1) + '_' +
|
||||
dirToken.column + '_' + dirToken.text;
|
||||
refToDef.set(refPos, {
|
||||
uri: fileUri,
|
||||
range: {
|
||||
start: {
|
||||
line: 0,
|
||||
character: 0
|
||||
},
|
||||
end: {
|
||||
line: Number.MAX_VALUE,
|
||||
character: Number.MAX_VALUE
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const tree = getFileContext(fileUri);
|
||||
const subDirScope: Scope = new FileScope(this.currentScope);
|
||||
this.currentScope = subDirScope;
|
||||
const definationListener = new DefinationListener(fileUri, subDirScope);
|
||||
antlr4.tree.ParseTreeWalker.DEFAULT.walk(definationListener, tree);
|
||||
}
|
||||
|
||||
exitAddSubDirCmd(ctx: any): void {
|
||||
if (!this.inBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.parseTreeProperty.get(ctx)) {
|
||||
this.currentScope = this.currentScope.getEnclosingScope();
|
||||
}
|
||||
}
|
||||
|
||||
enterOtherCmd(ctx: any): void {
|
||||
if (!this.inBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
// command reference, resolve the defination
|
||||
const cmdToken: Token = ctx.ID().symbol;
|
||||
const symbol: Sym = this.currentScope.resolve(cmdToken.text, Type.Function);
|
||||
if (symbol === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// token.line start from 1, so -1 first
|
||||
const refPos: string = this.funcMacroSym.getUri() + '_' + (cmdToken.line - 1) + '_' +
|
||||
cmdToken.column + '_' + cmdToken.text;
|
||||
|
||||
// add to refToDef
|
||||
refToDef.set(refPos, symbol.getLocation());
|
||||
}
|
||||
|
||||
enterArgument(ctx: any): void {
|
||||
if (!this.inBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.parent instanceof CMakeParser.FunctionCmdContext ||
|
||||
ctx.parent instanceof CMakeParser.MacroCmdContext) {
|
||||
if (ctx.getChildCount() !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add function/macro argument to current scope
|
||||
const token = ctx.start;
|
||||
const varSymbol: Sym = new Sym(token.text, Type.Variable,
|
||||
this.funcMacroSym.getUri(), token.line - 1, token.column);
|
||||
this.currentScope.define(varSymbol);
|
||||
|
||||
// just return after parse function/macro formal parameter
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// find all variable reference, resolve the defination, add to refToDef
|
||||
const argToken: Token = ctx.start;
|
||||
const regexp: RegExp = /\${(.*?)}/g;
|
||||
const matches = argToken.text.matchAll(regexp);
|
||||
for (let match of matches) {
|
||||
const varRef: string = match[1];
|
||||
const symbol: Sym = this.currentScope.resolve(varRef, Type.Variable);
|
||||
if (symbol === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// token.line start from 1, so - 1 first
|
||||
const refPos: string = this.funcMacroSym.getUri() + '_' + (argToken.line - 1) + '_' +
|
||||
(argToken.column + match.index + 2) + '_' + varRef;
|
||||
refToDef.set(refPos, symbol.getLocation());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import * as path from "path";
|
||||
import { Location } from "vscode-languageserver-types";
|
||||
import antlr4 from '../parser/antlr4/index.js';
|
||||
import Token from "../parser/antlr4/Token";
|
||||
import ParseTree from "../parser/antlr4/tree/ParseTree.js";
|
||||
import CMakeListener from "../parser/CMakeListener";
|
||||
import { getFileContext, getIncludeFileUri, getSubCMakeListsUri } from "../utils";
|
||||
import { FuncMacroListener } from "./function";
|
||||
import { FileScope, FunctionScope, Scope } from "./scope";
|
||||
import { Sym, Type } from "./symbol";
|
||||
|
||||
export const definations: Map<string, Location> = new Map();
|
||||
export const topScope: FileScope = new FileScope(null);
|
||||
|
||||
/**
|
||||
|
@ -20,21 +20,19 @@ export const refToDef: Map<string, Location> = new Map();
|
|||
|
||||
export class DefinationListener extends CMakeListener {
|
||||
private currentScope: Scope;
|
||||
private inFunction = false;
|
||||
private inBody = false;
|
||||
private uri: string;
|
||||
|
||||
private parseTreeProperty = new Map<ParseTree, boolean>();
|
||||
|
||||
constructor(uri: string, scope: Scope) {
|
||||
super();
|
||||
this.uri = uri;
|
||||
this.currentScope = scope;
|
||||
}
|
||||
|
||||
enterFile(ctx: any): void {
|
||||
|
||||
}
|
||||
|
||||
enterFunctionCmd(ctx: any): void {
|
||||
this.inFunction = true;
|
||||
this.inBody = true;
|
||||
|
||||
// create a function symbol
|
||||
const funcToken: Token = ctx.argument(0).start;
|
||||
|
@ -43,28 +41,33 @@ export class DefinationListener extends CMakeListener {
|
|||
|
||||
// add to current scope
|
||||
this.currentScope.define(funcSymbol);
|
||||
|
||||
// create a new function scope
|
||||
const funcScope: Scope = new FunctionScope(this.currentScope);
|
||||
this.currentScope = funcScope;
|
||||
|
||||
// add all remain arguments to function scope
|
||||
ctx.argument().slice(1).forEach(element => {
|
||||
const argToken = element.start;
|
||||
const varSymbol: Sym = new Sym(argToken.text, Type.Variable,
|
||||
this.uri, argToken.line - 1, argToken.column);
|
||||
this.currentScope.define(varSymbol);
|
||||
});
|
||||
}
|
||||
|
||||
exitEndFunctionCmd(ctx: any): void {
|
||||
this.inFunction = false;
|
||||
this.inBody = false;
|
||||
}
|
||||
|
||||
// restore the parent scope
|
||||
this.currentScope = this.currentScope.getEnclosingScope();
|
||||
enterMacroCmd(ctx: any): void {
|
||||
this.inBody = true;
|
||||
|
||||
// create a macro symbol
|
||||
const macroToken: Token = ctx.argument(0).start;
|
||||
const macroSymbol: Sym = new Sym(macroToken.text, Type.Macro,
|
||||
this.uri, macroToken.line - 1, macroToken.column);
|
||||
|
||||
// add macro to this scope
|
||||
this.currentScope.define(macroSymbol);
|
||||
}
|
||||
|
||||
exitEndMacroCmd(ctx: any): void {
|
||||
this.inBody = false;
|
||||
}
|
||||
|
||||
enterSetCmd(ctx: any): void {
|
||||
if (this.inBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create a variable symbol
|
||||
const varToken: Token = ctx.argument(0).start;
|
||||
const varSymbol: Sym = new Sym(varToken.text, Type.Variable,
|
||||
|
@ -79,6 +82,10 @@ export class DefinationListener extends CMakeListener {
|
|||
}
|
||||
|
||||
enterIncludeCmd(ctx: any): void {
|
||||
if (this.inBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nameToken = ctx.argument(0).start;
|
||||
const fileUri: string = getIncludeFileUri(this.uri, nameToken.text);
|
||||
if (!fileUri) {
|
||||
|
@ -108,12 +115,20 @@ export class DefinationListener extends CMakeListener {
|
|||
}
|
||||
|
||||
enterAddSubDirCmd(ctx: any): void {
|
||||
this.parseTreeProperty.set(ctx, false);
|
||||
|
||||
if (this.inBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dirToken: Token = ctx.argument(0).start;
|
||||
const fileUri: string = getSubCMakeListsUri(this.uri, dirToken.text);
|
||||
if (!fileUri) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.parseTreeProperty.set(ctx, true);
|
||||
|
||||
// add subdir CMakeLists.txt to refDef
|
||||
const refPos: string = this.uri + '_' + (dirToken.line - 1) + '_' +
|
||||
dirToken.column + '_' + dirToken.text;
|
||||
|
@ -139,25 +154,47 @@ export class DefinationListener extends CMakeListener {
|
|||
}
|
||||
|
||||
exitAddSubDirCmd(ctx: any): void {
|
||||
this.currentScope = this.currentScope.getEnclosingScope();
|
||||
if (this.inBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.parseTreeProperty.get(ctx)) {
|
||||
this.currentScope = this.currentScope.getEnclosingScope();
|
||||
}
|
||||
}
|
||||
|
||||
enterOtherCmd(ctx: any): void {
|
||||
if (this.inBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
// command reference, resolve the defination
|
||||
const cmdToken: Token = ctx.ID().symbol;
|
||||
const symbol: Sym = this.currentScope.resolve(cmdToken.text, Type.Function);
|
||||
if (symbol === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// token.line start from 1, so - 1 first
|
||||
const refPos: string = this.uri + '_' + (cmdToken.line - 1) + '_' +
|
||||
cmdToken.column + '_' + cmdToken.text;
|
||||
|
||||
// add to refToDef
|
||||
refToDef.set(refPos, symbol.getLocation());
|
||||
|
||||
// parse the function body
|
||||
const tree = getFileContext(symbol.getUri());
|
||||
const functionListener = new FuncMacroListener(this.currentScope, symbol);
|
||||
antlr4.tree.ParseTreeWalker.DEFAULT.walk(functionListener, tree);
|
||||
}
|
||||
|
||||
enterArgument(ctx: any): void {
|
||||
// If we are in function/macro body, just return
|
||||
// parse function/macro content just delay to reference
|
||||
if (this.inBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
const count: number = ctx.getChildCount();
|
||||
if (count !== 1) {
|
||||
return;
|
||||
|
|
|
@ -28,6 +28,7 @@ export class Scope {
|
|||
if (symbol.getType() === Type.Variable) {
|
||||
this.variables.set(symbol.getName(), symbol);
|
||||
} else {
|
||||
// Functions and Macros all saved into 'commands'
|
||||
this.commands.set(symbol.getName(), symbol);
|
||||
}
|
||||
|
||||
|
@ -37,6 +38,11 @@ export class Scope {
|
|||
getEnclosingScope(): Scope {
|
||||
return this.enclosingScope;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.variables.clear();
|
||||
this.commands.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export class FileScope extends Scope {
|
||||
|
@ -50,3 +56,9 @@ export class FunctionScope extends Scope {
|
|||
super(enclosingScope);
|
||||
}
|
||||
}
|
||||
|
||||
export class MacroScope extends Scope {
|
||||
constructor(enclosingScope: Scope) {
|
||||
super(enclosingScope);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,4 +53,16 @@ export class Sym {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
getUri(): string {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
getLine(): number {
|
||||
return this.line;
|
||||
}
|
||||
|
||||
getColumn(): number {
|
||||
return this.column;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue