This commit is contained in:
Griffin Downs 2019-08-08 09:28:18 -07:00 коммит произвёл Bob Brown
Родитель b608921b9d
Коммит ba2da8d98c
9 изменённых файлов: 232 добавлений и 3 удалений

Просмотреть файл

@ -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"
];