Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions api/vscode-clangd.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,19 @@ export interface ClangdApiV1 {
languageClient: BaseLanguageClient|undefined
}

export interface ClangdApiV2 {
// vscode-clangd's language clients keyed by workspace folder which can be used to send requests to the
// clangd language server(s)
// Standard requests:
// https://microsoft.github.io/language-server-protocol/specifications/specification-current
// clangd custom requests:
// https://clangd.llvm.org/extensions
languageClients: Map<string, BaseLanguageClient>
}

export interface ClangdExtension {
getApi(version: 1): ClangdApiV1;
getApi(version: 2): ClangdApiV2;
}

// clangd custom request types
Expand Down
14 changes: 9 additions & 5 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import {BaseLanguageClient} from 'vscode-languageclient';

import {ClangdApiV1, ClangdExtension} from '../api/vscode-clangd';
import {ClangdApiV1, ClangdApiV2, ClangdExtension} from '../api/vscode-clangd';
import {ClangdContext} from './clangd-context';

export class ClangdExtensionImpl implements ClangdExtension {
constructor(public client: BaseLanguageClient|undefined) {}
constructor(public context: ClangdContext) {}

public getApi(version: 1): ClangdApiV1;
public getApi(version: 2): ClangdApiV2;
public getApi(version: number): unknown {
if (version === 1) {
return {languageClient: this.client};
return {languageClient: this.context.clients.entries().next().value?.[1]};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we print out a warning when API1 is used in combination with multiple roots?

}

if (version === 2) {
return {languageClients: [...this.context.clients.values()]};
}

throw new Error(`No API version ${version} found`);
Expand Down
103 changes: 52 additions & 51 deletions src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,26 @@
import * as vscode from 'vscode';
import * as vscodelc from 'vscode-languageclient/node';

import {ClangdContext} from './clangd-context';
import {ClangdContext, ClangdLanguageClient} from './clangd-context';
import type {ASTParams, ASTNode} from '../api/vscode-clangd';

const ASTRequestMethod = 'textDocument/ast';

export function activate(context: ClangdContext) {
const feature = new ASTFeature(context);
context.client.registerFeature(feature);
}

const ASTRequestType =
new vscodelc.RequestType<ASTParams, ASTNode|null, void>(ASTRequestMethod);

class ASTFeature implements vscodelc.StaticFeature {
constructor(private context: ClangdContext) {
// The adapter holds the currently inspected node.
const adapter = new TreeAdapter();
// Create the AST view, showing data from the adapter.
const tree =
vscode.window.createTreeView('clangd.ast', {treeDataProvider: adapter});
context.subscriptions.push(
tree,
// Ensure the AST view is visible exactly when the adapter has a node.
// clangd.ast.hasData controls the view visibility (package.json).
adapter.onDidChangeTreeData((_) => {
vscode.commands.executeCommand('setContext', 'clangd.ast.hasData',
adapter.hasRoot());
// Work around https://github.com/microsoft/vscode/issues/90005
// Show the AST tree even if it's been collapsed or closed.
// reveal(root) fails here: "Data tree node not found".
if (adapter.hasRoot())
// @ts-ignore
tree.reveal(null);
}),
// Create the "Show AST" command for the context menu.
// It's only shown if the feature is dynamicaly available (package.json)
vscode.commands.registerTextEditorCommand(
'clangd.ast',
async (editor, _edit) => {
const converter = this.context.client.code2ProtocolConverter;
const item =
await this.context.client.sendRequest(ASTRequestType, {
textDocument:
converter.asTextDocumentIdentifier(editor.document),
range: converter.asRange(editor.selection),
});
if (!item)
vscode.window.showInformationMessage(
'No AST node at selection');
adapter.setRoot(item ?? undefined, editor.document.uri);
}),
// Clicking "close" will empty the adapter, which in turn hides the
// view.
vscode.commands.registerCommand(
'clangd.ast.close', () => adapter.setRoot(undefined, undefined)));
export class ASTFeature implements vscodelc.StaticFeature {
constructor(client: ClangdLanguageClient) {
client.registerFeature(this);
}

fillClientCapabilities(capabilities: vscodelc.ClientCapabilities) {}

// The "Show AST" command is enabled if the server advertises the capability.
initialize(capabilities: vscodelc.ServerCapabilities,
_documentSelector: vscodelc.DocumentSelector|undefined) {
vscode.commands.executeCommand('setContext', 'clangd.ast.supported',
'astProvider' in capabilities);
if ('astProvider' in capabilities)
vscode.commands.executeCommand('setContext', 'clangd.ast.supported', true);
}
getState(): vscodelc.FeatureState { return {kind: 'static'}; }
clear() {}
Expand Down Expand Up @@ -108,10 +64,55 @@ function describe(role: string, kind: string): string {
}

// Map a root ASTNode onto a VSCode tree.
class TreeAdapter implements vscode.TreeDataProvider<ASTNode> {
export class ASTProvider implements vscode.TreeDataProvider<ASTNode> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do these changes have to do with Multi-Root? I seem to be missing some context

private root?: ASTNode;
private doc?: vscode.Uri;

constructor(context: ClangdContext) {
// Create the AST view, showing data from the adapter.
const tree =
vscode.window.createTreeView('clangd.ast', {treeDataProvider: this});
context.subscriptions.push(
tree,
// Ensure the AST view is visible exactly when the adapter has a node.
// clangd.ast.hasData controls the view visibility (package.json).
this.onDidChangeTreeData((_) => {
vscode.commands.executeCommand('setContext', 'clangd.ast.hasData',
this.hasRoot());
// Work around https://github.com/microsoft/vscode/issues/90005
// Show the AST tree even if it's been collapsed or closed.
// reveal(root) fails here: "Data tree node not found".
if (this.hasRoot())
// @ts-ignore
tree.reveal(null);
}),
// Create the "Show AST" command for the context menu.
// It's only shown if the feature is dynamicaly available (package.json)
vscode.commands.registerTextEditorCommand(
'clangd.ast',
async (editor, _edit) => {
const client = context.getActiveClient();
if (client === undefined)
return;

const converter = client.code2ProtocolConverter;
const item =
await client.sendRequest(ASTRequestType, {
textDocument:
converter.asTextDocumentIdentifier(editor.document),
range: converter.asRange(editor.selection),
});
if (!item)
vscode.window.showInformationMessage(
'No AST node at selection');
this.setRoot(item ?? undefined, editor.document.uri);
}),
// Clicking "close" will empty the adapter, which in turn hides the
// view.
vscode.commands.registerCommand(
'clangd.ast.close', () => this.setRoot(undefined, undefined)));
}

hasRoot(): boolean { return this.root !== undefined; }

setRoot(newRoot: ASTNode|undefined, newDoc: vscode.Uri|undefined) {
Expand Down
Loading