Vcpkg integration (#3791)
This commit is contained in:
Родитель
b608921b9d
Коммит
ba2da8d98c
|
@ -519,6 +519,12 @@
|
|||
"default": "Enabled",
|
||||
"description": "If enabled, code is colorized based on IntelliSense. This setting has no effect if IntelliSense is disabled or if using the Default High Contrast theme.",
|
||||
"scope": "resource"
|
||||
},
|
||||
"C_Cpp.vcpkg.enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "Enable integration services for the [vcpkg dependency manager](https://aka.ms/vcpkg/).",
|
||||
"scope": "resource"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -627,6 +633,16 @@
|
|||
"command": "C_Cpp.RescanWorkspace",
|
||||
"title": "%c_cpp.command.rescanWorkspace.title%",
|
||||
"category": "C/C++"
|
||||
},
|
||||
{
|
||||
"command": "C_Cpp.VcpkgClipboardInstallSuggested",
|
||||
"title": "%c_cpp.command.vcpkgClipboardInstallSuggested.title%",
|
||||
"category": "C/C++"
|
||||
},
|
||||
{
|
||||
"command": "C_Cpp.VcpkgOnlineHelpSuggested",
|
||||
"title": "%c_cpp.command.vcpkgOnlineHelpSuggested.title%",
|
||||
"category": "C/C++"
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
|
|
|
@ -19,5 +19,7 @@
|
|||
"c_cpp.command.takeSurvey.title": "Partecipa al sondaggio",
|
||||
"c_cpp.command.buildAndDebugActiveFile.title": "Compila ed esegui il debug del file attivo",
|
||||
"c_cpp.command.logDiagnostics.title": "Registra diagnostica",
|
||||
"c_cpp.command.rescanWorkspace.title": "Ripetere l'analisi dell'area di lavoro"
|
||||
"c_cpp.command.rescanWorkspace.title": "Ripetere l'analisi dell'area di lavoro",
|
||||
"c_cpp.command.vcpkgClipboardInstallSuggested.title": "Copia il comando di installazione di vcpkg negli appunti",
|
||||
"c_cpp.command.vcpkgOnlineHelpSuggested.title": "Visita la pagina di aiuto di vcpkg"
|
||||
}
|
|
@ -19,5 +19,7 @@
|
|||
"c_cpp.command.takeSurvey.title": "Take Survey",
|
||||
"c_cpp.command.buildAndDebugActiveFile.title": "Build and Debug Active File",
|
||||
"c_cpp.command.logDiagnostics.title": "Log Diagnostics",
|
||||
"c_cpp.command.rescanWorkspace.title": "Rescan Workspace"
|
||||
"c_cpp.command.rescanWorkspace.title": "Rescan Workspace",
|
||||
"c_cpp.command.vcpkgClipboardInstallSuggested.title": "Copy vcpkg install command to clipboard",
|
||||
"c_cpp.command.vcpkgOnlineHelpSuggested.title": "Visit the vcpkg help page"
|
||||
}
|
|
@ -19,5 +19,7 @@
|
|||
"c_cpp.command.takeSurvey.title": "调查问卷",
|
||||
"c_cpp.command.buildAndDebugActiveFile.title": "生成和调试当前文件",
|
||||
"c_cpp.command.logDiagnostics.title": "记录诊断",
|
||||
"c_cpp.command.rescanWorkspace.title": "重新扫描工作区"
|
||||
"c_cpp.command.rescanWorkspace.title": "重新扫描工作区",
|
||||
"c_cpp.command.vcpkgClipboardInstallSuggested.title": "将vcpkg install命令复制到剪贴板",
|
||||
"c_cpp.command.vcpkgOnlineHelpSuggested.title": "访问vcpkg帮助页面"
|
||||
}
|
|
@ -234,6 +234,8 @@ export interface Client {
|
|||
logDiagnostics(): Promise<void>;
|
||||
rescanFolder(): Promise<void>;
|
||||
getCurrentConfigName(): Thenable<string>;
|
||||
getVcpkgInstalled(): Thenable<boolean>;
|
||||
getVcpkgEnabled(): Thenable<boolean>;
|
||||
getCurrentCompilerPathAndArgs(): Thenable<util.CompilerPathAndArgs>;
|
||||
getKnownCompilers(): Thenable<configs.KnownCompiler[]>;
|
||||
takeOwnership(document: vscode.TextDocument): void;
|
||||
|
@ -887,6 +889,16 @@ class DefaultClient implements Client {
|
|||
this.configuration.CurrentConfiguration.compilerArgs)
|
||||
));
|
||||
}
|
||||
|
||||
public getVcpkgInstalled(): Thenable<boolean> {
|
||||
return this.queueTask(() => Promise.resolve(this.configuration.VcpkgInstalled));
|
||||
}
|
||||
|
||||
public getVcpkgEnabled(): Thenable<boolean> {
|
||||
const cppSettings: CppSettings = new CppSettings(this.RootUri);
|
||||
return Promise.resolve(cppSettings.vcpkgEnabled);
|
||||
}
|
||||
|
||||
public getKnownCompilers(): Thenable<configs.KnownCompiler[]> {
|
||||
return this.queueTask(() => Promise.resolve(this.configuration.KnownCompiler));
|
||||
}
|
||||
|
@ -1666,6 +1678,8 @@ class NullClient implements Client {
|
|||
logDiagnostics(): Promise<void> { return Promise.resolve(); }
|
||||
rescanFolder(): Promise<void> { return Promise.resolve(); }
|
||||
getCurrentConfigName(): Thenable<string> { return Promise.resolve(""); }
|
||||
getVcpkgInstalled(): Thenable<boolean> { return Promise.resolve(false); }
|
||||
getVcpkgEnabled(): Thenable<boolean> { return Promise.resolve(false); }
|
||||
getCurrentCompilerPathAndArgs(): Thenable<util.CompilerPathAndArgs> { return Promise.resolve(undefined); }
|
||||
getKnownCompilers(): Thenable<configs.KnownCompiler[]> { return Promise.resolve([]); }
|
||||
takeOwnership(document: vscode.TextDocument): void {}
|
||||
|
|
|
@ -25,6 +25,9 @@ import { getTargetBuildInfo, BuildInfo } from '../githubAPI';
|
|||
import * as configs from './configurations';
|
||||
import { PackageVersion } from '../packageVersion';
|
||||
import { getTemporaryCommandRegistrarInstance } from '../commands';
|
||||
import * as rd from 'readline';
|
||||
import * as yauzl from 'yauzl';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
let prevCrashFile: string;
|
||||
let clients: ClientCollection;
|
||||
|
@ -42,8 +45,98 @@ let buildInfoCache: BuildInfo | null = null;
|
|||
const taskSourceStr: string = "C/C++";
|
||||
const cppInstallVsixStr: string = 'C/C++: Install vsix -- ';
|
||||
let taskProvider: vscode.Disposable;
|
||||
let codeActionProvider: vscode.Disposable;
|
||||
const intelliSenseDisabledError: string = "Do not activate the extension when IntelliSense is disabled.";
|
||||
|
||||
type vcpkgDatabase = { [key: string]: string[] }; // Stored as <header file entry> -> [<port name>]
|
||||
let vcpkgDbPromise: Promise<vcpkgDatabase>;
|
||||
function initVcpkgDatabase(): Promise<vcpkgDatabase> {
|
||||
return new Promise((resolve, reject) => {
|
||||
yauzl.open(util.getExtensionFilePath('VCPkgHeadersDatabase.zip'), { lazyEntries: true }, (err? : Error, zipfile?: yauzl.ZipFile) => {
|
||||
if (err) {
|
||||
resolve({});
|
||||
return;
|
||||
}
|
||||
zipfile.readEntry();
|
||||
let dbFound: boolean = false;
|
||||
zipfile.on('entry', entry => {
|
||||
if (entry.fileName !== 'VCPkgHeadersDatabase.txt') {
|
||||
return;
|
||||
}
|
||||
dbFound = true;
|
||||
zipfile.openReadStream(entry, (err?: Error, stream?: Readable) => {
|
||||
if (err) {
|
||||
resolve({});
|
||||
return;
|
||||
}
|
||||
let database: vcpkgDatabase = {};
|
||||
let reader: rd.ReadLine = rd.createInterface(stream);
|
||||
reader.on('line', (lineText: string) => {
|
||||
let portFilePair: string[] = lineText.split(':');
|
||||
if (portFilePair.length !== 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const portName: string = portFilePair[0];
|
||||
const relativeHeader: string = portFilePair[1];
|
||||
|
||||
if (!database[relativeHeader]) {
|
||||
database[relativeHeader] = [];
|
||||
}
|
||||
|
||||
database[relativeHeader].push(portName);
|
||||
});
|
||||
reader.on('close', () => {
|
||||
resolve(database);
|
||||
});
|
||||
});
|
||||
});
|
||||
zipfile.on('end', () => {
|
||||
if (!dbFound) {
|
||||
resolve({});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getVcpkgHelpAction(): vscode.CodeAction {
|
||||
const dummy: any[] = [{}]; // To distinguish between entry from CodeActions and the command palette
|
||||
return {
|
||||
command: { title: 'vcpkgOnlineHelpSuggested', command: 'C_Cpp.VcpkgOnlineHelpSuggested', arguments: dummy },
|
||||
title: "Learn how to install a library for this header with vcpkg",
|
||||
kind: vscode.CodeActionKind.QuickFix
|
||||
};
|
||||
}
|
||||
|
||||
function getVcpkgClipboardInstallAction(port: string): vscode.CodeAction {
|
||||
return {
|
||||
command: { title: 'vcpkgClipboardInstallSuggested', command: 'C_Cpp.VcpkgClipboardInstallSuggested', arguments: [[port]] },
|
||||
title: `Copy vcpkg command to install '${port}' to the clipboard`,
|
||||
kind: vscode.CodeActionKind.QuickFix
|
||||
};
|
||||
}
|
||||
|
||||
async function lookupIncludeInVcpkg(document: vscode.TextDocument, line: number): Promise<string[]> {
|
||||
const matches : RegExpMatchArray = document.lineAt(line).text.match(/#include\s*[<"](?<includeFile>[^>"]*)[>"]/);
|
||||
if (!matches.length) {
|
||||
return [];
|
||||
}
|
||||
const missingHeader: string = matches.groups['includeFile'].replace('/', '\\');
|
||||
|
||||
let portsWithHeader: string[];
|
||||
const vcpkgDb: vcpkgDatabase = await vcpkgDbPromise;
|
||||
if (vcpkgDb) {
|
||||
portsWithHeader = vcpkgDb[missingHeader];
|
||||
}
|
||||
return portsWithHeader ? portsWithHeader : [];
|
||||
}
|
||||
|
||||
function isMissingIncludeDiagnostic(diagnostic: vscode.Diagnostic): boolean {
|
||||
const missingIncludeCode: number = 1696;
|
||||
return diagnostic.code && diagnostic.code === missingIncludeCode && diagnostic.source && diagnostic.source === 'cpptools';
|
||||
}
|
||||
|
||||
/**
|
||||
* activate: set up the extension for language services
|
||||
*/
|
||||
|
@ -85,6 +178,33 @@ export function activate(activationEventOccurred: boolean): void {
|
|||
}
|
||||
});
|
||||
|
||||
const selector: vscode.DocumentSelector = [
|
||||
{ scheme: 'file', language: 'cpp' },
|
||||
{ scheme: 'file', language: 'c' }
|
||||
];
|
||||
codeActionProvider = vscode.languages.registerCodeActionsProvider(selector, {
|
||||
provideCodeActions: async (document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken): Promise<vscode.CodeAction[]> => {
|
||||
if (!await clients.ActiveClient.getVcpkgEnabled()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Generate vcpkg install/help commands if the incoming doc/range is a missing include error
|
||||
if (!context.diagnostics.some(isMissingIncludeDiagnostic)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
telemetry.logLanguageServerEvent('codeActionsProvided', { "source": "vcpkg" });
|
||||
|
||||
if (!await clients.ActiveClient.getVcpkgInstalled()) {
|
||||
return [getVcpkgHelpAction()];
|
||||
}
|
||||
|
||||
const ports: string[] = await lookupIncludeInVcpkg(document, range.start.line);
|
||||
const actions: vscode.CodeAction[] = ports.map<vscode.CodeAction>(getVcpkgClipboardInstallAction);
|
||||
return actions;
|
||||
}
|
||||
});
|
||||
|
||||
// handle "workspaceContains:/.vscode/c_cpp_properties.json" activation event.
|
||||
if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) {
|
||||
for (let i: number = 0; i < vscode.workspace.workspaceFolders.length; ++i) {
|
||||
|
@ -332,6 +452,8 @@ function realActivation(): void {
|
|||
|
||||
const settings: CppSettings = new CppSettings(clients.ActiveClient.RootUri);
|
||||
|
||||
vcpkgDbPromise = initVcpkgDatabase();
|
||||
|
||||
if (settings.updateChannel === 'Default') {
|
||||
suggestInsidersChannel();
|
||||
} else if (settings.updateChannel === 'Insiders') {
|
||||
|
@ -723,6 +845,8 @@ export function registerCommands(): void {
|
|||
disposables.push(vscode.commands.registerCommand('C_Cpp.TakeSurvey', onTakeSurvey));
|
||||
disposables.push(vscode.commands.registerCommand('C_Cpp.LogDiagnostics', onLogDiagnostics));
|
||||
disposables.push(vscode.commands.registerCommand('C_Cpp.RescanWorkspace', onRescanWorkspace));
|
||||
disposables.push(vscode.commands.registerCommand('C_Cpp.VcpkgClipboardInstallSuggested', onVcpkgClipboardInstallSuggested));
|
||||
disposables.push(vscode.commands.registerCommand('C_Cpp.VcpkgOnlineHelpSuggested', onVcpkgOnlineHelpSuggested));
|
||||
disposables.push(vscode.commands.registerCommand('cpptools.activeConfigName', onGetActiveConfigName));
|
||||
getTemporaryCommandRegistrarInstance().executeDelayedCommands();
|
||||
}
|
||||
|
@ -932,6 +1056,67 @@ function onTakeSurvey(): void {
|
|||
vscode.commands.executeCommand('vscode.open', uri);
|
||||
}
|
||||
|
||||
function onVcpkgOnlineHelpSuggested(dummy?: any): void {
|
||||
telemetry.logLanguageServerEvent('vcpkgAction', { 'source': dummy ? 'CodeAction' : 'CommandPalette', 'action': 'vcpkgOnlineHelpSuggested' });
|
||||
const uri: vscode.Uri = vscode.Uri.parse(`https://aka.ms/vcpkg`);
|
||||
vscode.commands.executeCommand('vscode.open', uri);
|
||||
}
|
||||
|
||||
async function onVcpkgClipboardInstallSuggested(ports?: string[]): Promise<void> {
|
||||
onActivationEvent();
|
||||
let source: string;
|
||||
if (ports && ports.length) {
|
||||
source = 'CodeAction';
|
||||
} else {
|
||||
source = 'CommandPalette';
|
||||
// Glob up all existing diagnostics for missing includes and look them up in the vcpkg database
|
||||
const missingIncludeLocations: [vscode.TextDocument, number[]][] = [];
|
||||
vscode.languages.getDiagnostics().forEach(uriAndDiagnostics => {
|
||||
// Extract textDocument
|
||||
const textDocument: vscode.TextDocument = vscode.workspace.textDocuments.find(doc => doc.uri.fsPath === uriAndDiagnostics[0].fsPath);
|
||||
if (!textDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract lines numbers for missing include diagnostics
|
||||
let lines: number[] = uriAndDiagnostics[1].filter(isMissingIncludeDiagnostic).map<number>(d => d.range.start.line);
|
||||
if (!lines.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter duplicate lines
|
||||
lines = lines.filter((line: number, index: number) => {
|
||||
const foundIndex: number = lines.indexOf(line);
|
||||
return foundIndex === index;
|
||||
});
|
||||
|
||||
missingIncludeLocations.push([textDocument, lines]);
|
||||
});
|
||||
if (!missingIncludeLocations.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue look ups in the vcpkg database for missing ports; filter out duplicate results
|
||||
let portsPromises: Promise<string[]>[] = [];
|
||||
missingIncludeLocations.forEach(docAndLineNumbers => {
|
||||
docAndLineNumbers[1].forEach(async line => {
|
||||
portsPromises.push(lookupIncludeInVcpkg(docAndLineNumbers[0], line));
|
||||
});
|
||||
});
|
||||
ports = [].concat(...(await Promise.all(portsPromises)));
|
||||
if (!ports.length) {
|
||||
return;
|
||||
}
|
||||
ports = ports.filter((port: string, index: number) => ports.indexOf(port) === index);
|
||||
}
|
||||
|
||||
let installCommand: string = 'vcpkg install';
|
||||
ports.forEach(port => installCommand += ` ${port}`);
|
||||
telemetry.logLanguageServerEvent('vcpkgAction', { 'source': source, 'action': 'vcpkgClipboardInstallSuggested', 'ports': ports.toString() });
|
||||
|
||||
await vscode.env.clipboard.writeText(installCommand);
|
||||
}
|
||||
|
||||
function onGetActiveConfigName(): Thenable<string> {
|
||||
return clients.ActiveClient.getCurrentConfigName();
|
||||
}
|
||||
|
@ -1075,6 +1260,9 @@ export function deactivate(): Thenable<void> {
|
|||
if (taskProvider) {
|
||||
taskProvider.dispose();
|
||||
}
|
||||
if (codeActionProvider) {
|
||||
codeActionProvider.dispose();
|
||||
}
|
||||
return clients.dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ export class CppSettings extends Settings {
|
|||
public get configurationWarnings(): string { return super.Section.get<string>("configurationWarnings"); }
|
||||
public get preferredPathSeparator(): string { return super.Section.get<string>("preferredPathSeparator"); }
|
||||
public get updateChannel(): string { return super.Section.get<string>("updateChannel"); }
|
||||
public get vcpkgEnabled(): boolean { return super.Section.get<boolean>("vcpkg.enabled"); }
|
||||
public get defaultIncludePath(): string[] { return super.Section.get<string[]>("default.includePath"); }
|
||||
public get defaultDefines(): string[] { return super.Section.get<string[]>("default.defines"); }
|
||||
public get defaultMacFrameworkPath(): string[] { return super.Section.get<string[]>("default.macFrameworkPath"); }
|
||||
|
|
|
@ -36,6 +36,8 @@ class TemporaryCommandRegistrar {
|
|||
"C_Cpp.TakeSurvey",
|
||||
"C_Cpp.LogDiagnostics",
|
||||
"C_Cpp.RescanWorkspace",
|
||||
"C_Cpp.VcpkgClipboardInstallSuggested",
|
||||
"C_Cpp.VcpkgOnlineHelpSuggested"
|
||||
];
|
||||
|
||||
constructor() {
|
||||
|
|
|
@ -369,6 +369,8 @@ function rewriteManifest(): Promise<void> {
|
|||
"onCommand:C_Cpp.TakeSurvey",
|
||||
"onCommand:C_Cpp.LogDiagnostics",
|
||||
"onCommand:C_Cpp.RescanWorkspace",
|
||||
"onCommand:C_Cpp.VcpkgClipboardInstallSuggested",
|
||||
"onCommand:C_Cpp.VcpkgClipboardOnlineHelpSuggested",
|
||||
"onDebug",
|
||||
"workspaceContains:/.vscode/c_cpp_properties.json"
|
||||
];
|
||||
|
|
Загрузка…
Ссылка в новой задаче