Skip to content
Open
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
9 changes: 9 additions & 0 deletions Extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3990,6 +3990,11 @@
"title": "%c_cpp.command.switchHeaderSource.title%",
"category": "C/C++"
},
{
"command": "C_Cpp.SelectTranslationUnit",
"title": "%c_cpp.command.selectTranslationUnit.title%",
"category": "C/C++"
},
{
"command": "C_Cpp.EnableErrorSquiggles",
"title": "%c_cpp.command.enableErrorSquiggles.title%",
Expand Down Expand Up @@ -6537,6 +6542,10 @@
"command": "C_Cpp.SwitchHeaderSource",
"when": "editorLangId =~ /^(c|(cuda-)?cpp)$/ && !(config.C_Cpp.intelliSenseEngine =~ /^[dD]isabled$/)"
},
{
"command": "C_Cpp.SelectTranslationUnit",
"when": "editorLangId =~ /^(c|(cuda-)?cpp)$/ && config.C_Cpp.intelliSenseEngine =~ /^[dD]efault$/"
},
{
"command": "C_Cpp.EnableErrorSquiggles",
"when": "config.C_Cpp.intelliSenseEngine =~ /^[dD]efault$/"
Expand Down
1 change: 1 addition & 0 deletions Extension/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"c_cpp.command.installCompiler.title": "Install a C++ Compiler",
"c_cpp.command.rescanCompilers.title": "Rescan for Compilers",
"c_cpp.command.switchHeaderSource.title": "Switch Header/Source",
"c_cpp.command.selectTranslationUnit.title": "Select a Translation Unit...",
"c_cpp.command.enableErrorSquiggles.title": "Enable Error Squiggles",
"c_cpp.command.disableErrorSquiggles.title": "Disable Error Squiggles",
"c_cpp.command.toggleDimInactiveRegions.title": "Toggle Inactive Region Colorization",
Expand Down
33 changes: 32 additions & 1 deletion Extension/src/LanguageServer/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,16 @@ interface SwitchHeaderSourceParams extends WorkspaceFolderParams {
switchHeaderSourceFileName: string;
}

interface GetTranslationUnitSourceCandidatesResult {
candidates: string[];
currentTranslationUnit: string;
}

interface SelectTranslationUnitParams {
uri: string;
translationUnit: string;
}

interface FileChangedParams extends WorkspaceFolderParams {
uri: string;
}
Expand Down Expand Up @@ -625,6 +635,7 @@ const PreInitializationRequest: RequestType<void, string, void> = new RequestTyp
const InitializationRequest: RequestType<CppInitializationParams, CppInitializationResult, void> = new RequestType<CppInitializationParams, CppInitializationResult, void>('cpptools/initialize');
const QueryCompilerDefaultsRequest: RequestType<QueryDefaultCompilerParams, configs.CompilerDefaults, void> = new RequestType<QueryDefaultCompilerParams, configs.CompilerDefaults, void>('cpptools/queryCompilerDefaults');
const SwitchHeaderSourceRequest: RequestType<SwitchHeaderSourceParams, string, void> = new RequestType<SwitchHeaderSourceParams, string, void>('cpptools/didSwitchHeaderSource');
const GetTranslationUnitSourceCandidatesRequest: RequestType<TextDocumentIdentifier, GetTranslationUnitSourceCandidatesResult, void> = new RequestType<TextDocumentIdentifier, GetTranslationUnitSourceCandidatesResult, void>('cpptools/getTranslationUnitSourceCandidates');
const GetDiagnosticsRequest: RequestType<void, GetDiagnosticsResult, void> = new RequestType<void, GetDiagnosticsResult, void>('cpptools/getDiagnostics');
export const GetDocumentSymbolRequest: RequestType<GetDocumentSymbolRequestParams, GetDocumentSymbolResult, void> = new RequestType<GetDocumentSymbolRequestParams, GetDocumentSymbolResult, void>('cpptools/getDocumentSymbols');
export const GetSymbolInfoRequest: RequestType<WorkspaceSymbolParams, LocalizeSymbolInformation[], void> = new RequestType<WorkspaceSymbolParams, LocalizeSymbolInformation[], void>('cpptools/getWorkspaceSymbols');
Expand Down Expand Up @@ -653,6 +664,7 @@ const PauseParsingNotification: NotificationType<void> = new NotificationType<vo
const ResumeParsingNotification: NotificationType<void> = new NotificationType<void>('cpptools/resumeParsing');
const DidChangeActiveEditorNotification: NotificationType<DidChangeActiveEditorParams> = new NotificationType<DidChangeActiveEditorParams>('cpptools/didChangeActiveEditor');
const RestartIntelliSenseForFileNotification: NotificationType<TextDocumentIdentifier> = new NotificationType<TextDocumentIdentifier>('cpptools/restartIntelliSenseForFile');
const SelectTranslationUnitNotification: NotificationType<SelectTranslationUnitParams> = new NotificationType<SelectTranslationUnitParams>('cpptools/selectTranslationUnit');
const DidChangeTextEditorSelectionNotification: NotificationType<Range> = new NotificationType<Range>('cpptools/didChangeTextEditorSelection');
const ChangeCompileCommandsNotification: NotificationType<FileChangedParams> = new NotificationType<FileChangedParams>('cpptools/didChangeCompileCommands');
const ChangeSelectedSettingNotification: NotificationType<FolderSelectedSettingParams> = new NotificationType<FolderSelectedSettingParams>('cpptools/didChangeSelectedSetting');
Expand Down Expand Up @@ -832,6 +844,8 @@ export interface Client {
takeOwnership(document: vscode.TextDocument): void;
sendDidOpen(document: vscode.TextDocument): Promise<void>;
requestSwitchHeaderSource(rootUri: vscode.Uri, fileName: string, token: vscode.CancellationToken): Thenable<string>;
getTranslationUnitSourceCandidates(uri: vscode.Uri, token: vscode.CancellationToken): Promise<GetTranslationUnitSourceCandidatesResult>;
selectTranslationUnit(uri: vscode.Uri, translationUnit: string): Promise<void>;
updateActiveDocumentTextOptions(): void;
didChangeActiveEditor(editor?: vscode.TextEditor, selection?: Range): Promise<void>;
restartIntelliSenseForFile(document: vscode.TextDocument): Promise<void>;
Expand Down Expand Up @@ -2759,7 +2773,7 @@ export class DefaultClient implements Client {
const isTrackedFile: boolean = hasNativeFileTypeMappings()
? isTagParsableFile(uri.fsPath)
: isTagParsableFile(uri.fsPath) ||
(ext !== undefined && this.associations_for_did_change?.has(ext.toLowerCase()) === true);
(ext !== undefined && this.associations_for_did_change?.has(ext.toLowerCase()) === true);
if (isTrackedFile) {
// VS Code has a bug that causes onDidChange events to happen to files that aren't changed,
// which causes a large backlog of "files to parse" to accumulate.
Expand Down Expand Up @@ -3083,6 +3097,19 @@ export class DefaultClient implements Client {
}
}

public async getTranslationUnitSourceCandidates(uri: vscode.Uri, token: vscode.CancellationToken): Promise<GetTranslationUnitSourceCandidatesResult> {
const params: TextDocumentIdentifier = { uri: uri.toString() };
await withCancellation(this.ready, token);
return DefaultClient.withLspCancellationHandling(
() => this.languageClient.sendRequest(GetTranslationUnitSourceCandidatesRequest, params, token), token);
}

public async selectTranslationUnit(uri: vscode.Uri, translationUnit: string): Promise<void> {
const params: SelectTranslationUnitParams = { uri: uri.toString(), translationUnit };
await this.ready;
return this.languageClient.sendNotification(SelectTranslationUnitNotification, params).catch(logAndReturn.undefined);
}

public async requestCompiler(newCompilerPath?: string): Promise<configs.CompilerDefaults> {
const params: QueryDefaultCompilerParams = {
newTrustedCompilerPath: newCompilerPath ?? ""
Expand Down Expand Up @@ -4455,6 +4482,10 @@ class NullClient implements Client {
takeOwnership(document: vscode.TextDocument): void { }
sendDidOpen(document: vscode.TextDocument): Promise<void> { return Promise.resolve(); }
requestSwitchHeaderSource(rootUri: vscode.Uri, fileName: string, token: vscode.CancellationToken): Thenable<string> { return Promise.resolve(""); }
getTranslationUnitSourceCandidates(uri: vscode.Uri, token: vscode.CancellationToken): Promise<GetTranslationUnitSourceCandidatesResult> {
return Promise.resolve({ candidates: [], currentTranslationUnit: "" });
}
selectTranslationUnit(uri: vscode.Uri, translationUnit: string): Promise<void> { return Promise.resolve(); }
updateActiveDocumentTextOptions(): void { }
didChangeActiveEditor(editor?: vscode.TextEditor): Promise<void> { return Promise.resolve(); }
restartIntelliSenseForFile(document: vscode.TextDocument): Promise<void> { return Promise.resolve(); }
Expand Down
113 changes: 103 additions & 10 deletions Extension/src/LanguageServer/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ export async function registerCommands(enabled: boolean): Promise<void> {
commandDisposables.forEach(d => d.dispose());
commandDisposables.length = 0;
commandDisposables.push(vscode.commands.registerCommand('C_Cpp.SwitchHeaderSource', enabled ? onSwitchHeaderSource : onDisabledCommand));
commandDisposables.push(vscode.commands.registerCommand('C_Cpp.SelectTranslationUnit', enabled ? onSelectTranslationUnit : onDisabledCommand));
commandDisposables.push(vscode.commands.registerCommand('C_Cpp.ResetDatabase', enabled ? onResetDatabase : onDisabledCommand));
commandDisposables.push(vscode.commands.registerCommand('C_Cpp.SelectIntelliSenseConfiguration', enabled ? selectIntelliSenseConfiguration : onDisabledCommand));
commandDisposables.push(vscode.commands.registerCommand('C_Cpp.InstallCompiler', enabled ? installCompiler : onDisabledCommand));
Expand Down Expand Up @@ -468,6 +469,107 @@ async function onRestartIntelliSenseForFile() {
return clients.ActiveClient.restartIntelliSenseForFile(activeEditor.document);
}

function getEditorPath(fileName: string): string {
let bestRealRoot: string | undefined;
let bestRootPath: string | undefined;
clients.forEach(client => {
if (!client.RootRealPath) {
return;
}
const relativePath: string = path.relative(client.RootRealPath, fileName);
const isUnderRoot: boolean = relativePath === "" ||
(relativePath !== ".." && !relativePath.startsWith(`..${path.sep}`) && !path.isAbsolute(relativePath));
if (isUnderRoot && (!bestRealRoot || client.RootRealPath.length > bestRealRoot.length)) {
bestRealRoot = client.RootRealPath;
bestRootPath = client.RootPath;
}
});
return bestRealRoot && bestRootPath ? path.join(bestRootPath, path.relative(bestRealRoot, fileName)) : fileName;
}

interface TranslationUnitQuickPickItem extends vscode.QuickPickItem {
translationUnit: string;
}

async function onSelectTranslationUnit(): Promise<void> {
const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
if (!activeEditor || !util.isCpp(activeEditor.document)) {
return;
}

const client: Client = clients.ActiveClient;
const result = await (async () => {
const tokenSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource();
try {
const candidatesPromise = client.getTranslationUnitSourceCandidates(activeEditor.document.uri, tokenSource.token);
const showProgress: boolean = await new Promise<boolean>((resolve, reject) => {
const timer: NodeJS.Timeout = global.setTimeout(() => resolve(true), 2000);
void candidatesPromise.then(() => {
clearTimeout(timer);
resolve(false);
}, (e) => {
clearTimeout(timer);
reject(e);
});
});

if (!showProgress) {
return await candidatesPromise;
}

return await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: localize('find.translation.units', 'Finding Translation Units...'),
cancellable: true
}, async (_progress, token) => {
const cancellationListener: vscode.Disposable = token.onCancellationRequested(() => tokenSource.cancel());
try {
return await candidatesPromise;
} finally {
cancellationListener.dispose();
}
});
} catch (e) {
if (e instanceof vscode.CancellationError) {
return undefined;
}
throw e;
} finally {
tokenSource.dispose();
}
})();
if (result === undefined) {
return;
}
if (result.candidates.length === 0) {
void vscode.window.showInformationMessage(localize('no.translation.units.found', 'No translation units were found for the active file.'));
return;
}
const currentTranslationUnit: string = getEditorPath(result.currentTranslationUnit);
const items: TranslationUnitQuickPickItem[] = result.candidates.map(candidate => {
const editorPath: string = getEditorPath(candidate);
const relativePath: string = vscode.workspace.asRelativePath(vscode.Uri.file(editorPath));
const directory: string = path.dirname(relativePath);
const isCurrent: boolean = editorPath === currentTranslationUnit;
return {
label: `${isCurrent ? "$(check) " : ""}${path.basename(relativePath)}`,
description: isCurrent ? localize('current.translation.unit', 'Current translation unit') :
directory === "." ? undefined : directory,
detail: editorPath,
translationUnit: editorPath
};
});
const selection: TranslationUnitQuickPickItem | undefined = await vscode.window.showQuickPick(items, {
Comment thread
sean-mcmanus marked this conversation as resolved.
title: localize('select.translation.unit', 'Select a Translation Unit'),
placeHolder: localize('select.translation.unit.placeholder', 'Select a source file to use as the translation unit'),
matchOnDescription: true,
matchOnDetail: true
});
if (selection !== undefined) {
await client.selectTranslationUnit(activeEditor.document.uri, selection.translationUnit);
}
}

async function onSwitchHeaderSource(): Promise<void> {
const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
if (!activeEditor || !util.isCpp(activeEditor.document)) {
Expand All @@ -487,16 +589,7 @@ async function onSwitchHeaderSource(): Promise<void> {
if (!targetFileName) {
return;
}
// If the targetFileName has a path that is a symlink target of a workspace folder,
// then replace the RootRealPath with the RootPath (the symlink path).
let targetFileNameReplaced: boolean = false;
clients.forEach(client => {
if (!targetFileNameReplaced && client.RootRealPath && client.RootPath !== client.RootRealPath
&& targetFileName.startsWith(client.RootRealPath)) {
targetFileName = client.RootPath + targetFileName.substring(client.RootRealPath.length);
targetFileNameReplaced = true;
}
});
targetFileName = getEditorPath(targetFileName);
const document: vscode.TextDocument = await vscode.workspace.openTextDocument(targetFileName);
await vscode.window.showTextDocument(document).then(undefined, logAndReturn.undefined);
} catch (e) {
Expand Down
8 changes: 8 additions & 0 deletions Extension/src/nativeStrings.json
Original file line number Diff line number Diff line change
Expand Up @@ -777,5 +777,13 @@
"browse_database_disabled_incompatible_storage": {
"text": "The browse database was disabled because its storage location does not support SQLite WAL shared memory. Set browse.databaseFilename to a local path.",
"hint": "browse.databaseFilename is a setting name. {Locked=\"SQLite WAL\"} {Locked=\"browse.databaseFilename\"}"
},
"selected_translation_unit_not_include_file_previous": {
"text": "The selected translation unit '{0}' does not include '{1}'. IntelliSense will restore the previous translation unit if it is still available; otherwise, it will use '{1}' as a header-only translation unit.",
"hint": "{0} is the selected translation unit path. {1} is the active file path. {Locked=\"IntelliSense\"} {Locked=\"{0}\"} {Locked=\"{1}\"}"
},
"selected_translation_unit_not_include_file_header_only": {
"text": "The selected translation unit '{0}' does not include '{1}'. No previous translation unit is available, so IntelliSense will use '{1}' as a header-only translation unit.",
"hint": "{0} is the selected translation unit path. {1} is the active file path. {Locked=\"IntelliSense\"} {Locked=\"{0}\"} {Locked=\"{1}\"}"
}
}
Loading