extension.ts
5.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import * as vscode from 'vscode';
import * as vscodelc from 'vscode-languageclient';
import * as semanticHighlighting from './semantic-highlighting';
/**
* Method to get workspace configuration option
* @param option name of the option (e.g. for clangd.path should be path)
* @param defaultValue default value to return if option is not set
*/
function getConfig<T>(option: string, defaultValue?: any): T {
const config = vscode.workspace.getConfiguration('clangd');
return config.get<T>(option, defaultValue);
}
namespace SwitchSourceHeaderRequest {
export const type =
new vscodelc.RequestType<vscodelc.TextDocumentIdentifier, string|undefined,
void, void>('textDocument/switchSourceHeader');
}
class FileStatus {
private statuses = new Map<string, any>();
private readonly statusBarItem =
vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10);
onFileUpdated(fileStatus: any) {
const filePath = vscode.Uri.parse(fileStatus.uri);
this.statuses.set(filePath.fsPath, fileStatus);
this.updateStatus();
}
updateStatus() {
const path = vscode.window.activeTextEditor.document.fileName;
const status = this.statuses.get(path);
if (!status) {
this.statusBarItem.hide();
return;
}
this.statusBarItem.text = `clangd: ` + status.state;
this.statusBarItem.show();
}
clear() {
this.statuses.clear();
this.statusBarItem.hide();
}
dispose() { this.statusBarItem.dispose(); }
}
class ClangdLanguageClient extends vscodelc.LanguageClient {
// Override the default implementation for failed requests. The default
// behavior is just to log failures in the output panel, however output panel
// is designed for extension debugging purpose, normal users will not open it,
// thus when the failure occurs, normal users doesn't know that.
//
// For user-interactive operations (e.g. applyFixIt, applyTweaks), we will
// prompt up the failure to users.
logFailedRequest(rpcReply: vscodelc.RPCMessageType, error: any) {
if (error instanceof vscodelc.ResponseError &&
rpcReply.method === "workspace/executeCommand")
vscode.window.showErrorMessage(error.message);
// Call default implementation.
super.logFailedRequest(rpcReply, error);
}
}
/**
* this method is called when your extension is activate
* your extension is activated the very first time the command is executed
*/
export function activate(context: vscode.ExtensionContext) {
const syncFileEvents = getConfig<boolean>('syncFileEvents', true);
const clangd: vscodelc.Executable = {
command : getConfig<string>('path'),
args : getConfig<string[]>('arguments')
};
const traceFile = getConfig<string>('trace');
if (!!traceFile) {
const trace = {CLANGD_TRACE : traceFile};
clangd.options = {env : {...process.env, ...trace}};
}
const serverOptions: vscodelc.ServerOptions = clangd;
const clientOptions: vscodelc.LanguageClientOptions = {
// Register the server for c-family and cuda files.
documentSelector: [
{ scheme: 'file', language: 'c' },
{ scheme: 'file', language: 'cpp' },
// cuda is not supported by vscode, but our extension does.
{ scheme: 'file', language: 'cuda' },
{ scheme: 'file', language: 'objective-c'},
{ scheme: 'file', language: 'objective-cpp'}
],
synchronize: !syncFileEvents ? undefined : {
// FIXME: send sync file events when clangd provides implementations.
},
initializationOptions: { clangdFileStatus: true },
// Do not switch to output window when clangd returns output
revealOutputChannelOn: vscodelc.RevealOutputChannelOn.Never
};
const clangdClient = new ClangdLanguageClient('Clang Language Server',
serverOptions, clientOptions);
if (getConfig<boolean>('semanticHighlighting')) {
const semanticHighlightingFeature =
new semanticHighlighting.SemanticHighlightingFeature(clangdClient,
context);
context.subscriptions.push(
vscode.Disposable.from(semanticHighlightingFeature));
clangdClient.registerFeature(semanticHighlightingFeature);
}
console.log('Clang Language Server is now active!');
context.subscriptions.push(clangdClient.start());
context.subscriptions.push(vscode.commands.registerCommand(
'clangd-vscode.switchheadersource', async () => {
const uri =
vscode.Uri.file(vscode.window.activeTextEditor.document.fileName);
if (!uri) {
return;
}
const docIdentifier =
vscodelc.TextDocumentIdentifier.create(uri.toString());
const sourceUri = await clangdClient.sendRequest(
SwitchSourceHeaderRequest.type, docIdentifier);
if (!sourceUri) {
return;
}
const doc = await vscode.workspace.openTextDocument(
vscode.Uri.parse(sourceUri));
vscode.window.showTextDocument(doc);
}));
const status = new FileStatus();
context.subscriptions.push(vscode.Disposable.from(status));
context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(
() => { status.updateStatus(); }));
context.subscriptions.push(clangdClient.onDidChangeState(({newState}) => {
if (newState == vscodelc.State.Running) {
// clangd starts or restarts after crash.
clangdClient.onNotification(
'textDocument/clangd.fileStatus',
(fileStatus) => { status.onFileUpdated(fileStatus); });
} else if (newState == vscodelc.State.Stopped) {
// Clear all cached statuses when clangd crashes.
status.clear();
}
}));
// An empty place holder for the activate command, otherwise we'll get an
// "command is not registered" error.
context.subscriptions.push(vscode.commands.registerCommand(
'clangd-vscode.activate', async () => {}));
}