From cbfb4f99e282d9d8ca0926f4daae2e34bd988a35 Mon Sep 17 00:00:00 2001 From: Yan Pashkovsky Date: Wed, 23 Jan 2019 15:29:13 +0300 Subject: [PATCH 1/2] dataflow --- package.json | 32 ++++++++++- src/dataFlowHierarchy.ts | 117 +++++++++++++++++++++++++++++++++++++++ src/serverContext.ts | 9 ++- 3 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 src/dataFlowHierarchy.ts diff --git a/package.json b/package.json index f122968..4f28c32 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,11 @@ "id": "ccls.callHierarchy", "name": "Call Hierarchy", "when": "extension.ccls.callHierarchyVisible" + }, + { + "id": "ccls.dataFlowInto", + "name": "Data Flow Hierarchy", + "when": "extension.ccls.dataFlowHierarchyVisible" } ] }, @@ -49,14 +54,19 @@ "group": "navigation@1.32" }, { - "command": "ccls.vars", + "command": "ccls.dataFlowInto", "when": "resourceLangId == cpp", "group": "navigation@1.33" }, { - "command": "ccls.base", + "command": "ccls.vars", "when": "resourceLangId == cpp", "group": "navigation@1.34" + }, + { + "command": "ccls.base", + "when": "resourceLangId == cpp", + "group": "navigation@1.35" } ], "view/title": [ @@ -69,6 +79,11 @@ "command": "ccls.closeCallHierarchy", "when": "view == ccls.callHierarchy", "group": "navigation" + }, + { + "command": "ccls.closeDataFlowHierarchy", + "when": "view == ccls.dataFlowInto", + "group": "navigation" } ], "view/item/context": [ @@ -86,6 +101,10 @@ "command": "ccls.closeCallHierarchy", "when": "false" }, + { + "command": "ccls.closeDataFlowHierarchy", + "when": "false" + }, { "command": "ccls.gotoForTreeView", "when": "false" @@ -131,6 +150,15 @@ "category": "ccls", "command": "ccls.vars" }, + { + "title": "Show Data Flow Into", + "category": "ccls", + "command": "ccls.dataFlowInto" + }, + { + "title": "Close", + "command": "ccls.closeDataFlowHierarchy" + }, { "title": "Show Cross References", "category": "ccls", diff --git a/src/dataFlowHierarchy.ts b/src/dataFlowHierarchy.ts new file mode 100644 index 0000000..4a13a4f --- /dev/null +++ b/src/dataFlowHierarchy.ts @@ -0,0 +1,117 @@ +import { + commands, + Disposable, + Event, + EventEmitter, + Position, + Range, + TextEditor, + TreeDataProvider, + TreeItem, + TreeItemCollapsibleState, + Uri, + workspace +} from "vscode"; +import { LanguageClient } from "vscode-languageclient/lib/main"; +import * as ls from "vscode-languageserver-types"; +import { Icon } from "./types"; +import { disposeAll, resourcePath, setContext } from "./utils"; + +export interface DataFlowHierarchyNode { + // These properties come directly from the language server. + id: number; + location: ls.Location; + children: DataFlowHierarchyNode[]; +} + +export class DataFlowHierarchyProvider implements TreeDataProvider, Disposable { + private readonly onDidChangeEmitter: EventEmitter = new EventEmitter(); + // tslint:disable-next-line:member-ordering + public readonly onDidChangeTreeData: Event = this.onDidChangeEmitter.event; + private root?: DataFlowHierarchyNode; + private icon: Icon; + private _dispose: Disposable[] = []; + + constructor( + readonly languageClient: LanguageClient, + ) { + this.icon = { + dark: resourcePath("base-dark.svg"), + light: resourcePath("base-light.svg") + }; + this._dispose.push(commands.registerCommand( + 'ccls.closeDataFlowHierarchy', this.closeHierarchy, this) + ); + this._dispose.push(commands.registerTextEditorCommand( + 'ccls.dataFlowInto', this.showHierarchy, this) + ); + } + + public dispose() { + return disposeAll(this._dispose); + } + + public async getTreeItem(element: DataFlowHierarchyNode): Promise { + let collapseState = TreeItemCollapsibleState.None; + if (element.children.length > 0) + collapseState = TreeItemCollapsibleState.Expanded; + + const parentFile = await workspace.openTextDocument(Uri.parse(element.location.uri)); + let label = parentFile.getText( + new Range( + new Position(element.location.range.start.line, element.location.range.start.character), + new Position(element.location.range.end.line, element.location.range.end.character) + ) + ); + + if (element.location) { + const path = Uri.parse(element.location.uri).path; + const name = path.substr(path.lastIndexOf("/") + 1); + label += ` (${name}:${element.location.range.start.line + 1})`; + } + + const ti = new TreeItem(label, collapseState); + ti.iconPath = this.icon; + ti.command = { + arguments: [element, element.children.length > 0], + command: 'ccls.hackGotoForTreeView', + title: 'Goto' + }; + ti.contextValue = 'cclsGoto'; + + return ti; + } + + public getChildren( + element?: DataFlowHierarchyNode + ): DataFlowHierarchyNode[] | Thenable { + if (!this.root) + return []; + if (!element) + return [this.root]; + return element.children; + } + + private closeHierarchy() { + setContext('extension.ccls.dataFlowHierarchyVisible', false); + this.root = undefined; + this.onDidChangeEmitter.fire(); + } + + private async showHierarchy(editor: TextEditor) { + setContext('extension.ccls.dataFlowHierarchyVisible', true); + const position = editor.selection.active; + const uri = editor.document.uri; + const callNode = await this.languageClient.sendRequest( + '$ccls/dataFlowInto', + { + position, + textDocument: { + uri: uri.toString(true), + }, + } + ); + this.root = callNode; + this.onDidChangeEmitter.fire(); + } +} diff --git a/src/serverContext.ts b/src/serverContext.ts index 901e117..bce7c6e 100644 --- a/src/serverContext.ts +++ b/src/serverContext.ts @@ -28,6 +28,7 @@ import { Converter } from "vscode-languageclient/lib/protocolConverter"; import * as ls from "vscode-languageserver-types"; import { CallHierarchyNode, CallHierarchyProvider } from "./callHierarchy"; import { CclsErrorHandler } from "./cclsErrorHandler"; +import { DataFlowHierarchyNode, DataFlowHierarchyProvider } from "./dataFlowHierarchy"; import { cclsChan } from './globalContext'; import { InactiveRegionsProvider } from "./inactiveRegions"; import { InheritanceHierarchyNode, InheritanceHierarchyProvider } from "./inheritanceHierarchy"; @@ -271,6 +272,12 @@ export class ServerContext implements Disposable { "ccls.callHierarchy", callHierarchyProvider )); + const dfProvier = new DataFlowHierarchyProvider(this.client); + this._dispose.push(dfProvier); + this._dispose.push(window.registerTreeDataProvider( + 'ccls.dataFlowInto', dfProvier + )); + // Common between tree views. this._dispose.push(commands.registerCommand( "ccls.gotoForTreeView", this.gotoForTreeView, this @@ -613,7 +620,7 @@ export class ServerContext implements Disposable { } private async hackGotoForTreeView( - node: InheritanceHierarchyNode|CallHierarchyNode, + node: InheritanceHierarchyNode|CallHierarchyNode|DataFlowHierarchyNode, hasChildren: boolean ) { if (!node.location) From 6add9c65e1702c7551c7d18eae7b4bb9b9912b4e Mon Sep 17 00:00:00 2001 From: Yan Pashkovsky Date: Wed, 23 Jan 2019 16:24:18 +0300 Subject: [PATCH 2/2] show explorer --- src/dataFlowHierarchy.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dataFlowHierarchy.ts b/src/dataFlowHierarchy.ts index 4a13a4f..fb16c3a 100644 --- a/src/dataFlowHierarchy.ts +++ b/src/dataFlowHierarchy.ts @@ -113,5 +113,6 @@ export class DataFlowHierarchyProvider implements TreeDataProvider