Skip to content

Commit

Permalink
Support installation of clangd binaries if not found.
Browse files Browse the repository at this point in the history
Also has auto-update checks, but turned off by default for now.

See:
- Library with main logic: clangd/node-clangd@3ce82ac
- PR for vscode-clangd: clangd/vscode-clangd#5
  • Loading branch information
sam-mccall committed May 5, 2020
1 parent a6e30a1 commit 7994348
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 43 deletions.
16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@
"default": false,
"description": "Enable semantic highlighting in clangd, requires jackguo380/vim-lsp-cxx-highlight to work"
},
"clangd.checkUpdates": {
"type": "boolean",
"default": false,
"description": "Check for clangd language server updates on startup."
},
"suggest.detailMaxLength": {
"type": "number",
"description": "Max length of detail that should be shown in popup menu.",
Expand All @@ -109,7 +114,18 @@
{
"command": "clangd.symbolInfo",
"title": "Resolve symbol info under the cursor"
},
{
"command": "clangd.install",
"title": "Install latest clangd language server binary release"
},
{
"command": "clangd.update",
"title": "Check for updates to clangd language server"
}
]
},
"dependencies": {
"@clangd/install": "^0.1.1"
}
}
4 changes: 0 additions & 4 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ export class Config {
return this.cfg.get('disableDiagnostics') as boolean;
}

get path() {
return this.cfg.get('path', 'clangd');
}

get arguments() {
return this.cfg.get<string[]>('arguments', []);
}
Expand Down
15 changes: 0 additions & 15 deletions src/ctx.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { Executable, ExtensionContext, LanguageClient, LanguageClientOptions, ServerOptions, services, StaticFeature, workspace } from 'coc.nvim';
import { existsSync } from 'fs';
import { TextDocumentClientCapabilities } from 'vscode-languageserver-protocol';
import which from 'which';
import { Config } from './config';
import { SemanticHighlightingFeature } from './semantic-highlighting';

Expand All @@ -22,19 +20,6 @@ export class Ctx {
this.config = new Config();
}

resolveBin(): string | undefined {
const bin = which.sync(this.config.path, { nothrow: true });
if (!bin) {
return;
}

if (!existsSync(bin)) {
return;
}

return bin;
}

async startServer(bin: string) {
const old = this.client;
if (old) {
Expand Down
14 changes: 7 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@ import { commands, ExtensionContext, services, State, workspace } from 'coc.nvim
import * as cmds from './cmds';
import { Ctx } from './ctx';
import { FileStatus } from './file_status';
import * as install from './install';

export async function activate(context: ExtensionContext): Promise<void> {
const ctx = new Ctx(context);
if (!ctx.config.enabled) {
return;
}

const bin = ctx.resolveBin();
if (!bin) {
workspace.showMessage(`clangd is not found, you need to install clangd first. https://clangd.llvm.org/installation.html`, 'error');
return;
}

for (const service of services.getServiceStats()) {
if (service.id.includes('clangd')) {
workspace.showMessage(`Looks like you've configured clangd in coc-settings.json, you should remove it to use coc-clangd`, 'warning');
return;
}
}

const clangdPath = await install.activate(context);
if (!clangdPath) {
return;
}

try {
await ctx.startServer(bin);
await ctx.startServer(clangdPath);
} catch (e) {
return;
}
Expand Down
64 changes: 64 additions & 0 deletions src/install.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as coc from 'coc.nvim';
import * as common from '@clangd/install';

class UI {
constructor(private context: coc.ExtensionContext, private config: coc.WorkspaceConfiguration) {}

get storagePath(): string {
return this.context.storagePath;
}
slow<T>(title: string, result: Promise<T>) {
coc.workspace.showMessage(title + '...');
return result;
}
error(s: string) {
coc.workspace.showMessage(s, 'error');
}
info(s: string) {
coc.workspace.showMessage(s);
}
progress<T>(title: string, _cancel: any, body: (progress: (fraction: number) => void) => Promise<T>) {
return this.slow(
title,
body(() => {})
);
}

async shouldReuse(release: string) {
coc.workspace.showMessage(`Reusing existing ${release} installation in ${this.storagePath}`);
return true;
}
promptReload(message: string) {
message += " Type ':CocRestart' to reload.";
coc.workspace.showMessage(message);
}
showHelp(message: string, url: string) {
message += ` See ${url}.`;
coc.workspace.showMessage(message);
}
async promptUpdate(oldVersion: string, newVersion: string) {
const message = `clangd ${newVersion} is available (you have ${oldVersion}). :CocCommand clangd.install, or :CocSettings to disable clangd.checkUpdates.`;
coc.workspace.showMessage(message);
}
async promptInstall(version: string) {
const message = `clangd was not found on your PATH. :CocCommand clangd.install will install ${version}.`;
coc.workspace.showMessage(message);
}

get clangdPath(): string {
return this.config.get<string>('path')!;
}
set clangdPath(p: string) {
this.config.update('path', p, /*isUser=*/ true);
}
}

// Returns the clangd path to use, or null if clangd is not installed.
export async function activate(context: coc.ExtensionContext): Promise<string | null> {
const cfg = coc.workspace.getConfiguration('clangd');
const ui = new UI(context, cfg);
context.subscriptions.push(coc.commands.registerCommand('clangd.install', async () => common.installLatest(ui)));
context.subscriptions.push(coc.commands.registerCommand('clangd.update', async () => common.checkUpdates(true, ui)));
const status = await common.prepare(ui, cfg.get<boolean>('checkUpdates', false));
return status.clangdPath;
}
Loading

0 comments on commit 7994348

Please sign in to comment.