Skip to content

Commit 5223701

Browse files
committed
Add basic plumbing for Flutter Outline.
See #605.
1 parent 670f00e commit 5223701

5 files changed

Lines changed: 112 additions & 2 deletions

File tree

‎package.json‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
"icon": "media/icon.png",
3333
"activationEvents": [
3434
"onLanguage:dart",
35-
"onView:dartPackages",
3635
"workspaceContains:**/pubspec.yaml",
3736
"workspaceContains:**/*.dart",
3837
"onCommand:flutter.createProject",
@@ -376,6 +375,11 @@
376375
},
377376
"views": {
378377
"explorer": [
378+
{
379+
"id": "dartFlutterOutline",
380+
"name": "Flutter Outline",
381+
"when": "dart-code:flutterProjectLoaded && dart-code:showFlutterOutline"
382+
},
379383
{
380384
"id": "dartPackages",
381385
"name": "Dependencies",

‎src/analysis/analyzer.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export class AnalyzerCapabilities {
2121
get supportsClosingLabels() { return versionIsAtLeast(this.version, "1.18.4"); }
2222
get supportsGetDeclerations() { return versionIsAtLeast(this.version, "1.18.7"); }
2323
get supportsGetDeclerationsForFile() { return versionIsAtLeast(this.version, "1.19.0"); }
24+
get supportsFlutterOutline() { return versionIsAtLeast(this.version, "1.19.0"); }
2425
get isDart2() { return versionIsAtLeast(this.version, "1.19.0"); }
2526
}
2627

‎src/analysis/open_file_tracker.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ export class OpenFileTracker implements Disposable {
7171
},
7272
}).then(() => { }, util.logError); // tslint:disable-line:no-empty
7373
}
74+
75+
if (this.analyzer.capabilities.supportsFlutterOutline) {
76+
this.analyzer.flutterSetSubscriptions({
77+
subscriptions: {
78+
OUTLINE: priorityFiles,
79+
},
80+
});
81+
}
7482
}
7583

7684
public static getOutlineFor(file: Uri): Outline | undefined {

‎src/extension.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import { setUpHotReloadOnSave } from "./flutter/hot_reload_save_handler";
4646
import { findPackageRoots } from "./analysis/utils";
4747
import { flutterPath, dartVMPath, analyzerSnapshotPath, handleMissingSdks, findSdks } from "./sdk/utils";
4848
import { GoToSuperCommand } from "./commands/go_to_super";
49+
import { FlutterOutlineProvider } from "./views/flutter_outline_view";
4950

5051
const DART_MODE: vs.DocumentFilter[] = [{ language: "dart", scheme: "file" }];
5152
const HTML_MODE: vs.DocumentFilter[] = [{ language: "html", scheme: "file" }];
@@ -230,6 +231,9 @@ export function activate(context: vs.ExtensionContext, isRestart: boolean = fals
230231
context.subscriptions.push(vs.languages.registerDocumentSymbolProvider(filter, documentSymbolProvider));
231232
});
232233

234+
if (analyzer.capabilities.supportsFlutterOutline)
235+
context.subscriptions.push(vs.window.registerTreeDataProvider("dartFlutterOutline", new FlutterOutlineProvider(analyzer)));
236+
233237
context.subscriptions.push(new OpenFileTracker(analyzer));
234238
});
235239

@@ -257,7 +261,7 @@ export function activate(context: vs.ExtensionContext, isRestart: boolean = fals
257261
context.subscriptions.push(new TypeHierarchyCommand(analyzer));
258262
context.subscriptions.push(new GoToSuperCommand(analyzer));
259263

260-
// Register our view providers.
264+
// Register our dependency tree provider.
261265
const dartPackagesProvider = new DartPackagesProvider();
262266
dartPackagesProvider.setWorkspaces(util.getDartWorkspaceFolders());
263267
context.subscriptions.push(dartPackagesProvider);

‎src/views/flutter_outline_view.ts‎

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"use strict";
2+
3+
import * as vs from "vscode";
4+
import * as as from "../analysis/analysis_server_types";
5+
import { Analyzer } from "../analysis/analyzer";
6+
import { isAnalyzable } from "../utils";
7+
8+
const DART_SHOW_FLUTTER_OUTLINE = "dart-code:showFlutterOutline";
9+
10+
export class FlutterOutlineProvider implements vs.TreeDataProvider<FlutterWidgetItem>, vs.Disposable {
11+
private subscriptions: vs.Disposable[] = [];
12+
private analyzer: Analyzer;
13+
private activeEditor: vs.TextEditor;
14+
private flutterOutline: as.FlutterOutlineNotification;
15+
private updateTimeout: NodeJS.Timer;
16+
private onDidChangeTreeDataEmitter: vs.EventEmitter<FlutterWidgetItem | undefined> = new vs.EventEmitter<FlutterWidgetItem | undefined>();
17+
public readonly onDidChangeTreeData: vs.Event<FlutterWidgetItem | undefined> = this.onDidChangeTreeDataEmitter.event;
18+
19+
constructor(analyzer: Analyzer) {
20+
this.analyzer = analyzer;
21+
22+
this.analyzer.registerForFlutterOutline((n) => {
23+
if (this.activeEditor && n.file === this.activeEditor.document.fileName) {
24+
this.flutterOutline = n;
25+
// Delay this so if we're getting lots of updates we don't flicker.
26+
clearTimeout(this.updateTimeout);
27+
this.updateTimeout = setTimeout(() => this.update(), 500);
28+
}
29+
});
30+
31+
this.subscriptions.push(vs.window.onDidChangeActiveTextEditor((e) => this.setTrackingFile(e)));
32+
if (vs.window.activeTextEditor)
33+
this.setTrackingFile(vs.window.activeTextEditor);
34+
}
35+
36+
private update() {
37+
if (!this.flutterOutline || !this.activeEditor || this.flutterOutline.file !== this.activeEditor.document.fileName)
38+
return;
39+
40+
FlutterOutlineProvider.showTree();
41+
42+
// Do tree...
43+
}
44+
45+
private setTrackingFile(editor: vs.TextEditor) {
46+
if (editor && isAnalyzable(editor.document)) {
47+
this.activeEditor = editor;
48+
this.flutterOutline = null;
49+
50+
this.analyzer.forceNotificationsFor(editor.document.fileName);
51+
} else {
52+
FlutterOutlineProvider.hideTree();
53+
this.activeEditor = null;
54+
}
55+
}
56+
57+
public refresh(): void {
58+
this.onDidChangeTreeDataEmitter.fire();
59+
}
60+
61+
public getTreeItem(element: FlutterWidgetItem): vs.TreeItem {
62+
return element;
63+
}
64+
65+
public getChildren(element?: FlutterWidgetItem): FlutterWidgetItem[] {
66+
return [];
67+
}
68+
69+
private static setTreeVisible(visible: boolean) {
70+
vs.commands.executeCommand("setContext", DART_SHOW_FLUTTER_OUTLINE, visible);
71+
}
72+
73+
public static showTree() { this.setTreeVisible(true); }
74+
public static hideTree() { this.setTreeVisible(false); }
75+
76+
public dispose() {
77+
this.activeEditor = null;
78+
this.subscriptions.forEach((s) => s.dispose());
79+
}
80+
}
81+
82+
class FlutterWidgetItem extends vs.TreeItem {
83+
constructor(
84+
public readonly label: string,
85+
public readonly depPath: string,
86+
public readonly collapsibleState?: vs.TreeItemCollapsibleState,
87+
public readonly command?: vs.Command,
88+
) {
89+
super(label, collapsibleState);
90+
}
91+
92+
public contextValue = "flutterWidget";
93+
}

0 commit comments

Comments
 (0)