refactor: Goto defination

This commit is contained in:
全卓 2022-11-15 18:53:49 +08:00
parent 56db02cc7d
commit b549508f3a
7 changed files with 99 additions and 70 deletions

View File

@ -10,7 +10,8 @@
"license": "MIT",
"dependencies": {
"vscode-languageserver": "^8.0.2",
"vscode-languageserver-textdocument": "^1.0.7"
"vscode-languageserver-textdocument": "^1.0.7",
"vscode-uri": "^3.0.6"
},
"engines": {
"node": "*"
@ -53,6 +54,11 @@
"version": "3.17.2",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz",
"integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA=="
},
"node_modules/vscode-uri": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.6.tgz",
"integrity": "sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ=="
}
},
"dependencies": {
@ -87,6 +93,11 @@
"version": "3.17.2",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz",
"integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA=="
},
"vscode-uri": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.6.tgz",
"integrity": "sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ=="
}
}
}

View File

@ -9,10 +9,11 @@
},
"repository": {
"type": "git",
"url": "https://gitee.com/openkylin/cmake-intellisence"
"url": "https://gitee.com/openkylin/cmake-intellisence"
},
"dependencies": {
"vscode-languageserver": "^8.0.2",
"vscode-languageserver-textdocument": "^1.0.7"
"vscode-languageserver-textdocument": "^1.0.7",
"vscode-uri": "^3.0.6"
}
}

View File

@ -22,7 +22,10 @@ import CMakeLexer from './parser/CMakeLexer.js';
import CMakeParser from './parser/CMakeParser.js';
import { SymbolListener } from './docSymbols';
import { Entries, getBuiltinEntries, getCMakeVersion, getFileContext } from './utils';
import { DefinationListener, refToDef, topScope } from './symbolTable/goToDefination';
import { DefinationListener, incToBaseDir, refToDef, topScope } from './symbolTable/goToDefination';
import { existsSync } from 'fs';
import { URI, Utils } from 'vscode-uri';
import path = require('path');
type Word = {
text: string,
@ -261,18 +264,26 @@ connection.onDefinition((params: DefinitionParams) => {
}
return new Promise((resolve, reject) => {
const uri: string = params.textDocument.uri;
const tree = getFileContext(uri);
const definationListener = new DefinationListener(uri, topScope);
let rootFile = workspaceFolders[0].uri + '/CMakeLists.txt';
let rootFileURI: URI = URI.parse(rootFile);
if (!existsSync(rootFileURI.fsPath)) {
rootFile = params.textDocument.uri;
rootFileURI = URI.parse(rootFile);
}
const baseDir: URI = Utils.dirname(rootFileURI);
const tree = getFileContext(rootFileURI);
const definationListener = new DefinationListener(baseDir, rootFileURI, topScope);
// clear refToDef and topScope first
refToDef.clear();
topScope.clear();
incToBaseDir.clear();
antlr4.tree.ParseTreeWalker.DEFAULT.walk(definationListener, tree);
const document = documents.get(uri);
const document = documents.get(params.textDocument.uri);
const word: Word = getWordAtPosition(document, params.position);
const wordPos: string = uri + '_' + params.position.line + '_' +
const wordPos: string = params.textDocument.uri + '_' + params.position.line + '_' +
word.col + '_' + word.text;
if (refToDef.has(wordPos)) {

View File

@ -1,12 +1,13 @@
import Token from "../parser/antlr4/Token";
import CMakeListener from "../parser/CMakeListener";
import CMakeParser from "../parser/CMakeParser";
import { DefinationListener, refToDef } from "./goToDefination";
import { DefinationListener, incToBaseDir, 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";
import { URI } from "vscode-uri";
export class FuncMacroListener extends CMakeListener {
private currentScope: Scope;
@ -78,8 +79,11 @@ export class FuncMacroListener extends CMakeListener {
}
const nameToken = ctx.argument(0).start;
const fileUri: string = getIncludeFileUri(this.funcMacroSym.getUri(), nameToken.text);
if (!fileUri) {
// 获取包含该函数定义的文件的基路径
const baseDir: URI = incToBaseDir.get(this.funcMacroSym.getUri().fsPath);
const incUri: URI = getIncludeFileUri(baseDir, nameToken.text);
if (!incUri) {
return;
}
@ -87,7 +91,7 @@ export class FuncMacroListener extends CMakeListener {
const refPos: string = this.funcMacroSym.getUri() + '_' + (nameToken.line - 1) + '_' +
nameToken.column + '_' + nameToken.text;
refToDef.set(refPos, {
uri: fileUri,
uri: incUri.toString(),
range: {
start: {
line: 0,
@ -100,8 +104,8 @@ export class FuncMacroListener extends CMakeListener {
}
});
const tree = getFileContext(fileUri);
const definationListener = new DefinationListener(fileUri, this.currentScope);
const tree = getFileContext(incUri);
const definationListener = new DefinationListener(baseDir, incUri, this.currentScope);
antlr4.tree.ParseTreeWalker.DEFAULT.walk(definationListener, tree);
}
@ -112,8 +116,9 @@ export class FuncMacroListener extends CMakeListener {
}
const dirToken: Token = ctx.argument(0).start;
const fileUri: string = getSubCMakeListsUri(this.funcMacroSym.getUri(), dirToken.text);
if (!fileUri) {
const baseDir: URI = incToBaseDir.get(this.funcMacroSym.getUri().fsPath);
const subCMakeListsUri: URI = getSubCMakeListsUri(baseDir, dirToken.text);
if (!subCMakeListsUri) {
return;
}
@ -123,7 +128,7 @@ export class FuncMacroListener extends CMakeListener {
const refPos: string = this.funcMacroSym.getUri() + '_' + (dirToken.line - 1) + '_' +
dirToken.column + '_' + dirToken.text;
refToDef.set(refPos, {
uri: fileUri,
uri: subCMakeListsUri.toString(),
range: {
start: {
line: 0,
@ -136,10 +141,10 @@ export class FuncMacroListener extends CMakeListener {
}
});
const tree = getFileContext(fileUri);
const tree = getFileContext(subCMakeListsUri);
const subDirScope: Scope = new FileScope(this.currentScope);
this.currentScope = subDirScope;
const definationListener = new DefinationListener(fileUri, subDirScope);
const definationListener = new DefinationListener(baseDir, subCMakeListsUri, subDirScope);
antlr4.tree.ParseTreeWalker.DEFAULT.walk(definationListener, tree);
}

View File

@ -1,14 +1,16 @@
import { Location } from "vscode-languageserver-types";
import { URI, Utils } from "vscode-uri";
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 { FileScope, Scope } from "./scope";
import { Sym, Type } from "./symbol";
export const topScope: FileScope = new FileScope(null);
export const incToBaseDir: Map<string, URI> = new Map<string, URI>();
/**
* key: <uri>_<line>_<column>_<word>
@ -21,14 +23,17 @@ export const refToDef: Map<string, Location> = new Map();
export class DefinationListener extends CMakeListener {
private currentScope: Scope;
private inBody = false;
private uri: string;
private curFile: URI; // current file uri
private baseDir: URI; // directory used by include/add_subdirectory commands
private parseTreeProperty = new Map<ParseTree, boolean>();
constructor(uri: string, scope: Scope) {
constructor(baseDir: URI, curFile: URI, scope: Scope) {
super();
this.uri = uri;
this.curFile = curFile;
this.currentScope = scope;
this.baseDir = baseDir;
// Utils.dirname(URI.parse(uri)).fsPath
}
enterFunctionCmd(ctx: any): void {
@ -37,7 +42,7 @@ export class DefinationListener extends CMakeListener {
// create a function symbol
const funcToken: Token = ctx.argument(0).start;
const funcSymbol: Sym = new Sym(funcToken.text, Type.Function,
this.uri, funcToken.line - 1, funcToken.column);
this.curFile, funcToken.line - 1, funcToken.column);
// add to current scope
this.currentScope.define(funcSymbol);
@ -53,7 +58,7 @@ export class DefinationListener extends CMakeListener {
// 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);
this.curFile, macroToken.line - 1, macroToken.column);
// add macro to this scope
this.currentScope.define(macroSymbol);
@ -71,7 +76,7 @@ export class DefinationListener extends CMakeListener {
// create a variable symbol
const varToken: Token = ctx.argument(0).start;
const varSymbol: Sym = new Sym(varToken.text, Type.Variable,
this.uri, varToken.line - 1, varToken.column);
this.curFile, varToken.line - 1, varToken.column);
// add variable to current scope
this.currentScope.define(varSymbol);
@ -87,16 +92,18 @@ export class DefinationListener extends CMakeListener {
}
const nameToken = ctx.argument(0).start;
const fileUri: string = getIncludeFileUri(this.uri, nameToken.text);
if (!fileUri) {
const incUri: URI = getIncludeFileUri(this.baseDir, nameToken.text);
if (!incUri) {
return;
}
incToBaseDir.set(incUri.toString(), this.baseDir);
// add included module to refDef
const refPos: string = this.uri + '_' + (nameToken.line - 1) + '_' +
const refPos: string = this.curFile + '_' + (nameToken.line - 1) + '_' +
nameToken.column + '_' + nameToken.text;
refToDef.set(refPos, {
uri: fileUri,
uri: incUri.toString(),
range: {
start: {
line: 0,
@ -109,8 +116,8 @@ export class DefinationListener extends CMakeListener {
}
});
const tree = getFileContext(fileUri);
const definationListener = new DefinationListener(fileUri, this.currentScope);
const tree = getFileContext(incUri);
const definationListener = new DefinationListener(this.baseDir, incUri, this.currentScope);
antlr4.tree.ParseTreeWalker.DEFAULT.walk(definationListener, tree);
}
@ -122,18 +129,18 @@ export class DefinationListener extends CMakeListener {
}
const dirToken: Token = ctx.argument(0).start;
const fileUri: string = getSubCMakeListsUri(this.uri, dirToken.text);
if (!fileUri) {
const subCMakeListsUri: URI = getSubCMakeListsUri(this.baseDir, dirToken.text);
if (!subCMakeListsUri) {
return;
}
this.parseTreeProperty.set(ctx, true);
// add subdir CMakeLists.txt to refDef
const refPos: string = this.uri + '_' + (dirToken.line - 1) + '_' +
const refPos: string = this.curFile + '_' + (dirToken.line - 1) + '_' +
dirToken.column + '_' + dirToken.text;
refToDef.set(refPos, {
uri: fileUri,
uri: subCMakeListsUri.toString(),
range: {
start: {
line: 0,
@ -146,10 +153,12 @@ export class DefinationListener extends CMakeListener {
}
});
const tree = getFileContext(fileUri);
const tree = getFileContext(subCMakeListsUri);
const subDirScope: Scope = new FileScope(this.currentScope);
// FIXME: 此处是否应该切换作用域?
this.currentScope = subDirScope;
const definationListener = new DefinationListener(fileUri, subDirScope);
const subBaseDir: URI = Utils.joinPath(this.baseDir, dirToken.text);
const definationListener = new DefinationListener(subBaseDir, subCMakeListsUri, subDirScope);
antlr4.tree.ParseTreeWalker.DEFAULT.walk(definationListener, tree);
}
@ -176,7 +185,7 @@ export class DefinationListener extends CMakeListener {
}
// token.line start from 1, so - 1 first
const refPos: string = this.uri + '_' + (cmdToken.line - 1) + '_' +
const refPos: string = this.curFile + '_' + (cmdToken.line - 1) + '_' +
cmdToken.column + '_' + cmdToken.text;
// add to refToDef
@ -216,7 +225,7 @@ export class DefinationListener extends CMakeListener {
}
// token.line start from 1, so - 1 first
const refPos: string = this.uri + '_' + (argToken.line - 1) + '_' +
const refPos: string = this.curFile + '_' + (argToken.line - 1) + '_' +
(argToken.column + match.index + 2) + '_' + varRef;
refToDef.set(refPos, symbol.getLocation());
}

View File

@ -1,4 +1,5 @@
import { Location } from 'vscode-languageserver-types';
import { URI } from 'vscode-uri';
import { Scope } from './scope';
export enum Type {
@ -14,11 +15,11 @@ export class Sym {
private type: Type;
private scope: Scope; // all symbols know what scope contains them
private name: string;
private uri: string;
private uri: URI;
private line: number;
private column: number;
constructor(name: string, type: Type, uri: string, line: number, column: number) {
constructor(name: string, type: Type, uri: URI, line: number, column: number) {
this.name = name;
this.type = type;
this.uri = uri;
@ -40,7 +41,7 @@ export class Sym {
getLocation(): Location {
return {
uri: this.uri,
uri: this.uri.toString(),
range: {
start: {
line: this.line,
@ -54,7 +55,7 @@ export class Sym {
};
}
getUri(): string {
getUri(): URI {
return this.uri;
}

View File

@ -1,15 +1,15 @@
import * as cp from 'child_process';
import { documents } from './server';
import { existsSync, fstat } from 'fs';
import { existsSync } from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as fs from 'fs';
import { fileURLToPath, pathToFileURL } from 'url';
import antlr4 from './parser/antlr4/index.js';
import CMakeLexer from "./parser/CMakeLexer";
import CMakeParser from "./parser/CMakeParser";
import InputStream from './parser/antlr4/InputStream';
import { URI, Utils } from 'vscode-uri';
export type Entries = [string, string, string, string];
@ -44,13 +44,13 @@ export function getCMakeVersion(): CMakeVersion {
};
}
export function getFileContext(uri: string) {
const document = documents.get(uri);
export function getFileContext(uri: URI) {
const document = documents.get(uri.toString());
let text: string;
if (document) {
text = document.getText();
} else {
text = fs.readFileSync(fileURLToPath(uri), { encoding: 'utf-8' });
text = fs.readFileSync(uri.fsPath, { encoding: 'utf-8' });
}
const input: InputStream = antlr4.CharStreams.fromString(text);
const lexer = new CMakeLexer(input);
@ -59,31 +59,21 @@ export function getFileContext(uri: string) {
return parser.file();
}
export function getSubCMakeListsUri(currentFileUri: string, subDir: string): string {
const currentFilePath: string = fileURLToPath(currentFileUri);
const subCMakeListsPath = path.join(currentFilePath, '..', subDir, 'CMakeLists.txt');
if (existsSync(subCMakeListsPath)) {
return pathToFileURL(subCMakeListsPath).toString();
export function getSubCMakeListsUri(baseDir: URI, subDir: string): URI {
const subCMakeListsUri: URI = Utils.joinPath(baseDir, subDir, 'CMakeLists.txt');
if (existsSync(subCMakeListsUri.fsPath)) {
return subCMakeListsUri;
}
return null;
}
export function getIncludeFileUri(currentFileUri: string, includeFileName: string): string {
const currentFilePath: string = fileURLToPath(currentFileUri);
const includeFilePath: string = path.dirname(currentFilePath) + path.sep + includeFileName;
// const includeFileUri: string = filePathToURL;
if (existsSync(includeFilePath)) {
const index = currentFileUri.lastIndexOf('/');
const includeFileUri = currentFileUri.slice(0, index) + path.sep + includeFileName;
return includeFileUri;
export function getIncludeFileUri(baseDir: URI, includeFileName: string): URI {
const incFileUri: URI = Utils.joinPath(baseDir, includeFileName);
if (existsSync(incFileUri.fsPath)) {
return incFileUri;
}
// TODO: if includeFileName contains variable reference
// ex: include(${CMAKE_CURRENT_LIST_DIR}/xxx.cmake)
// Module: AndroidTestUtilities
// name is a cmake module
const cmakePath: string = which('cmake');
if (cmakePath === null) {
return null;
@ -93,7 +83,8 @@ export function getIncludeFileUri(currentFileUri: string, includeFileName: strin
const resPath = path.join(cmakePath, '../..', 'share', moduleDir, 'Modules', includeFileName) + '.cmake';
if (existsSync(resPath)) {
return pathToFileURL(resPath).toString();
// return pathToFileURL(resPath).toString();
return URI.file(resPath);
}
return null;