refactor: Goto defination
This commit is contained in:
parent
56db02cc7d
commit
b549508f3a
|
@ -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=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue