Skip to content

Commit 6a83d90

Browse files
use document symbols for navigation
1 parent 28c4e29 commit 6a83d90

File tree

2 files changed

+101
-13
lines changed

2 files changed

+101
-13
lines changed

src/documentation/DocumentationManager.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@ export class DocumentationManager {
5252
this.previewEditor = undefined;
5353
}),
5454
];
55+
} else {
56+
this.previewEditor.reveal();
5557
}
5658
}
5759

58-
async buildDocumentation(folderContext: FolderContext): Promise<string | undefined> {
60+
private async buildDocumentation(folderContext: FolderContext): Promise<string | undefined> {
5961
const buildPath = path.join(folderContext.folder.fsPath, ".build", "vscode-swift");
6062
const outputPath = path.join(buildPath, "documentation-preview");
6163
await fs.rm(outputPath, { recursive: true, force: true });

src/documentation/DocumentationPreviewEditor.ts

Lines changed: 98 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import * as path from "path";
1818
import { WebviewMessage } from "./webview/WebviewMessage";
1919
import { WorkspaceContext } from "../WorkspaceContext";
2020
import { Target } from "../SwiftPackage";
21+
import { fileExists } from "../utilities/filesystem";
2122

2223
export class DocumentationPreviewEditor implements vscode.Disposable {
2324
private readonly webviewPanel: vscode.WebviewPanel;
@@ -49,25 +50,35 @@ export class DocumentationPreviewEditor implements vscode.Disposable {
4950
const scriptURI = this.webviewPanel.webview.asWebviewUri(
5051
vscode.Uri.file(this.extension.asAbsolutePath("assets/documentation-webview/index.js"))
5152
);
52-
this.currentRoute =
53-
this.extractPathFromTextDocument(vscode.window.activeTextEditor) ??
54-
getDefaultDocumentationPath(this.context);
55-
fs.readFile(path.join(archivePath, "index.html"), "utf-8").then(documentationHTML => {
53+
this.currentRoute = getDefaultDocumentationPath(this.context);
54+
Promise.all([
55+
this.findRouteFromTextDocument(vscode.window.activeTextEditor),
56+
fs.readFile(path.join(archivePath, "index.html"), "utf-8"),
57+
]).then(([activeTextEditorRoute, documentationHTML]) => {
58+
if (activeTextEditorRoute) {
59+
this.currentRoute = activeTextEditorRoute;
60+
}
5661
documentationHTML = documentationHTML
5762
.replaceAll("{{BASE_PATH}}", webviewBaseURI.toString())
5863
.replace("</body>", `<script src="${scriptURI.toString()}"></script></body>`);
5964
this.webviewPanel.webview.html = documentationHTML;
6065
this.subscriptions.push(
6166
this.webviewPanel.webview.onDidReceiveMessage(this.receiveMessage.bind(this)),
6267
vscode.window.onDidChangeActiveTextEditor(this.activeTextEditorChanged.bind(this)),
68+
vscode.window.onDidChangeTextEditorSelection(
69+
this.textEditorSelectionChanged.bind(this)
70+
),
6371
this.webviewPanel.onDidDispose(this.dispose.bind(this))
6472
);
65-
6673
// Reveal the editor, but don't change the focus of the active text editor
6774
this.webviewPanel.reveal(undefined, true);
6875
});
6976
}
7077

78+
reveal() {
79+
this.webviewPanel.reveal();
80+
}
81+
7182
dispose() {
7283
this.subscriptions.forEach(subscription => subscription.dispose());
7384
this.subscriptions = [];
@@ -94,15 +105,28 @@ export class DocumentationPreviewEditor implements vscode.Disposable {
94105
}
95106

96107
private activeTextEditorChanged(editor: vscode.TextEditor | undefined) {
97-
const navigateToPath = this.extractPathFromTextDocument(editor);
98-
if (!navigateToPath) {
99-
return;
100-
}
108+
this.findRouteFromTextDocument(editor).then(navigateToPath => {
109+
if (!navigateToPath) {
110+
return;
111+
}
101112

102-
this.postMessage({ type: "navigate", route: navigateToPath });
113+
this.postMessage({ type: "navigate", route: navigateToPath });
114+
});
103115
}
104116

105-
private extractPathFromTextDocument(editor: vscode.TextEditor | undefined): string | undefined {
117+
private textEditorSelectionChanged(event: vscode.TextEditorSelectionChangeEvent) {
118+
this.findRouteFromTextDocument(event.textEditor).then(navigateToPath => {
119+
if (!navigateToPath) {
120+
return;
121+
}
122+
123+
this.postMessage({ type: "navigate", route: navigateToPath });
124+
});
125+
}
126+
127+
private async findRouteFromTextDocument(
128+
editor: vscode.TextEditor | undefined
129+
): Promise<string | undefined> {
106130
const document = editor?.document;
107131
if (!document || document.uri.scheme !== "file") {
108132
return undefined;
@@ -113,9 +137,71 @@ export class DocumentationPreviewEditor implements vscode.Disposable {
113137
return;
114138
}
115139

116-
const targetRoute = `/documentation/${target.name.toLocaleLowerCase()}`;
140+
const targetRoute = `/documentation/${target.name}`;
141+
if (
142+
document.uri.fsPath.endsWith(".md") &&
143+
(await fileExists(
144+
this.archivePath,
145+
"data",
146+
"documentation",
147+
target.name.toLocaleLowerCase(),
148+
path.basename(document.uri.fsPath, ".md") + ".json"
149+
))
150+
) {
151+
return targetRoute + "/" + path.basename(document.uri.fsPath, ".md");
152+
} else if (document.uri.fsPath.endsWith(".tutorial")) {
153+
return `/tutorials/${target.name}/${path.basename(document.uri.fsPath, ".tutorial").replaceAll(" ", "-")}`;
154+
} else if (document.languageId === "swift") {
155+
const symbolRoute = await this.findSymbolInDocument(document, editor.selection);
156+
if (symbolRoute) {
157+
return targetRoute + "/" + symbolRoute;
158+
}
159+
}
117160
return targetRoute;
118161
}
162+
163+
private async findSymbolInDocument(
164+
document: vscode.TextDocument,
165+
range: vscode.Range
166+
): Promise<string | undefined> {
167+
const symbols: (vscode.SymbolInformation | vscode.DocumentSymbol)[] =
168+
await vscode.commands.executeCommand(
169+
"vscode.executeDocumentSymbolProvider",
170+
document.uri
171+
);
172+
for (const symbol of symbols) {
173+
if (!("children" in symbol)) {
174+
continue;
175+
}
176+
const symbolRoute = this.searchSymbol(symbol, range);
177+
if (symbolRoute) {
178+
return symbolRoute;
179+
}
180+
}
181+
return undefined;
182+
}
183+
184+
private searchSymbol(
185+
symbol: vscode.DocumentSymbol,
186+
range: vscode.Range,
187+
context?: string
188+
): string | undefined {
189+
if (!symbol.range.contains(range)) {
190+
return;
191+
}
192+
if (!context) {
193+
context = symbol.name;
194+
} else {
195+
context += "/" + symbol.name;
196+
}
197+
for (const child of symbol.children) {
198+
const childResult = this.searchSymbol(child, range, context);
199+
if (childResult) {
200+
return childResult;
201+
}
202+
}
203+
return context;
204+
}
119205
}
120206

121207
function findTargetForFile(file: string, ctx: WorkspaceContext): Target | undefined {

0 commit comments

Comments
 (0)