Compare commits

..
Author SHA1 Message Date
spebl 212ea02e4c Fix extract to function formatting. (#11804)
* Fix extract to function formatting.
2023-12-29 11:15:32 -08:00
Sean McManus ab8b90b187 Stop clearing code analysis problems in updateCustomConfigurations. (#11798) 2023-12-21 17:35:51 -08:00
Sean McManus 8d42c49b77 Fix configuration provider handling with intelliSenseEngine "disabled". (#11797) 2023-12-21 12:27:41 -08:00
Colen Garoutte-Carson 0e24296f67 Enable progressive IntelliSense updates (#11735) 2023-12-19 18:24:05 -08:00
browntarik 7f87dc49f8 Fix shell quoting for command line arguments (#11734)
* Add shell quoting for command line arguments

* add shell escape logic from native

* resolve lint issues

* Refactor quoteArgument and getTask

* Add os specific shell quoting

* Correctly apply shell-quotes on Unix

* Fix lint

* fix lint

* Refactor quoteArgument logic
2023-11-30 10:01:19 -08:00
Sean McManus cc4177b096 Update for 1.19.1 (#11724) 2023-11-21 14:58:42 -08:00
Sean McManus a25b309ac7 Update for 1.19.0 (#11688)
* Update for 1.19.0
2023-11-16 17:01:15 -08:00
Sean McManus b6f48cd1fe Update changelog and version for 1.18.5 (#11697) 2023-11-16 16:39:31 -08:00
Sean McManus ea391a3a17 Fix default Linux cache path. (#11696) 2023-11-16 15:46:03 -08:00
31 changed files with 47025 additions and 46717 deletions
+20 -1
View File
@@ -1,9 +1,28 @@
# C/C++ for Visual Studio Code Changelog
## Version 1.19.1: November 21, 2023
### Bug Fixes
* Fix `Add '#include'` code actions for Mac frameworks. [#11579](https://github.com/microsoft/vscode-cpptools/issues/11579)
* Fix snippet and include completion. [#11715](https://github.com/microsoft/vscode-cpptools/issues/11715), [#11720](https://github.com/microsoft/vscode-cpptools/issues/11720)
## Version 1.19.0: November 16, 2023
### Bug Fixes
* Fix IntelliSense bug with type deduction using concepts. [#8132](https://github.com/microsoft/vscode-cpptools/issues/8132)
* Fix clang-format error messages not being logged. [#8944](https://github.com/microsoft/vscode-cpptools/issues/8944)
* Fix insert mode sometimes doing a replace for completion. [#10613](https://github.com/microsoft/vscode-cpptools/issues/10613)
* Fix indentation missing in markdown fenced code blocks. [#11379](https://github.com/microsoft/vscode-cpptools/issues/11379)
* Fix the parent path of the source file in compile_commands.json not being added to the browse.path. [#11631](https://github.com/microsoft/vscode-cpptools/issues/11631)
* Fix the database not getting updated in certain cases when switching configurations. [#11649](https://github.com/microsoft/vscode-cpptools/issues/11649)
* Fix a cpptools crash with certain projects. [#11674](https://github.com/microsoft/vscode-cpptools/issues/11674)
## Version 1.18.5: November 16, 2023
### Bug Fix
* Fix `~/vscode-cpptools` being used as the cache folder instead of `~/.cache/vscode-cpptools` on Linux. [#11693](https://github.com/microsoft/vscode-cpptools/issues/11693)
## Version 1.18.4: November 14, 2023
### Bug Fixes:
* Fix 'Extract to function' not scrolling to and selecting the added header declaration. [#11676](https://github.com/microsoft/vscode-cpptools/issues/11676)
* Fix the extension sometimes failing to activate with VS Code versions less than 1.18. [#11680](https://github.com/microsoft/vscode-cpptools/issues/11680)
* Fix the extension sometimes failing to activate with VS Code versions less than 1.85. [#11680](https://github.com/microsoft/vscode-cpptools/issues/11680)
## Version 1.18.3: November 13, 2023
### New Features
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "cpptools",
"displayName": "C/C++",
"description": "C/C++ IntelliSense, debugging, and code browsing.",
"version": "1.18.4-main",
"version": "1.19.1-main",
"publisher": "ms-vscode",
"icon": "LanguageCCPP_color_128x.png",
"readme": "README.md",
@@ -7,7 +7,6 @@ import * as vscode from 'vscode';
import { Position, Range, RequestType, TextDocumentIdentifier } from 'vscode-languageclient';
import * as Telemetry from '../../telemetry';
import { DefaultClient, workspaceReferences } from '../client';
import { processDelayedDidOpen } from '../extension';
import { CancellationSender } from '../references';
import { makeVscodeRange } from '../utils';
@@ -104,7 +103,7 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider {
}
public async prepareCallHierarchy(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.CallHierarchyItem | undefined> {
await this.client.enqueue(() => processDelayedDidOpen(document));
await this.client.ready;
workspaceReferences.cancelCurrentReferenceRequest(CancellationSender.NewRequest);
workspaceReferences.clearViews();
@@ -4,7 +4,7 @@
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { Client, DefaultClient, GetDocumentSymbolRequest, GetDocumentSymbolRequestParams, GetDocumentSymbolResult, LocalizeDocumentSymbol, SymbolScope } from '../client';
import { clients, processDelayedDidOpen } from '../extension';
import { clients } from '../extension';
import { getLocalizedString, getLocalizedSymbolScope } from '../localization';
import { makeVscodeRange } from '../utils';
@@ -57,7 +57,7 @@ export class DocumentSymbolProvider implements vscode.DocumentSymbolProvider {
const client: Client = clients.getClientFor(document.uri);
if (client instanceof DefaultClient) {
const defaultClient: DefaultClient = <DefaultClient>client;
await client.enqueue(() => processDelayedDidOpen(document));
await client.ready;
const params: GetDocumentSymbolRequestParams = {
uri: document.uri.toString()
};
@@ -3,28 +3,65 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { ManualPromise } from '../../Utility/Async/manualPromise';
import { CppFoldingRange, DefaultClient, FoldingRangeKind, GetFoldingRangesParams, GetFoldingRangesRequest, GetFoldingRangesResult } from '../client';
import { processDelayedDidOpen } from '../extension';
import { CppSettings } from '../settings';
interface FoldingRangeRequestInfo {
promise: ManualPromise<vscode.FoldingRange[] | undefined> | undefined;
}
export class FoldingRangeProvider implements vscode.FoldingRangeProvider {
private client: DefaultClient;
public onDidChangeFoldingRangesEvent = new vscode.EventEmitter<void>();
public onDidChangeFoldingRanges?: vscode.Event<void>;
// Mitigate an issue where VS Code sends us an inordinate number of requests
// for the same file without waiting for the prior request to complete or cancelling them.
private pendingRequests: Map<string, FoldingRangeRequestInfo> = new Map<string, FoldingRangeRequestInfo>();
constructor(client: DefaultClient) {
this.client = client;
this.onDidChangeFoldingRanges = this.onDidChangeFoldingRangesEvent.event;
}
async provideFoldingRanges(document: vscode.TextDocument, context: vscode.FoldingContext, token: vscode.CancellationToken): Promise<vscode.FoldingRange[] | undefined> {
await this.client.ready;
const settings: CppSettings = new CppSettings();
if (!settings.codeFolding) {
return [];
}
const params: GetFoldingRangesParams = {
uri: document.uri.toString()
};
await this.client.enqueue(() => processDelayedDidOpen(document));
const pendingRequest: FoldingRangeRequestInfo | undefined = this.pendingRequests.get(document.uri.toString());
if (pendingRequest !== undefined) {
if (pendingRequest.promise === undefined) {
pendingRequest.promise = new ManualPromise<vscode.FoldingRange[] | undefined>();
}
console.log("Redundant folding ranges request received for: " + document.uri.toString());
return pendingRequest.promise;
}
const foldingRangeRequestInfo: FoldingRangeRequestInfo = {
promise: undefined
};
this.pendingRequests.set(document.uri.toString(), foldingRangeRequestInfo);
const promise: Promise<vscode.FoldingRange[] | undefined> = this.requestRanges(document.uri.toString(), token);
await promise;
this.pendingRequests.delete(document.uri.toString());
if (foldingRangeRequestInfo.promise !== undefined) {
promise.then(() => {
foldingRangeRequestInfo.promise?.resolve(promise);
}, () => {
foldingRangeRequestInfo.promise?.reject(new vscode.CancellationError());
});
}
return promise;
}
private async requestRanges(uri: string, token: vscode.CancellationToken): Promise<vscode.FoldingRange[] | undefined>
{
const params: GetFoldingRangesParams = {
uri
};
const response: GetFoldingRangesResult = await this.client.languageClient.sendRequest(GetFoldingRangesRequest, params, token);
if (token.isCancellationRequested || response.ranges === undefined) {
@@ -55,6 +92,14 @@ export class FoldingRangeProvider implements vscode.FoldingRangeProvider {
}
public refresh(): void {
// Consider all pending requests as being outdated. Cancel them all.
const oldPendingRequests: Map<string, FoldingRangeRequestInfo> = this.pendingRequests;
this.pendingRequests = new Map<string, FoldingRangeRequestInfo>();
this.onDidChangeFoldingRangesEvent.fire();
oldPendingRequests.forEach((value: FoldingRangeRequestInfo | undefined, _key: string) => {
if (value !== undefined && value.promise !== undefined) {
value.promise.reject(new vscode.CancellationError());
}
});
}
}
@@ -3,22 +3,19 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { Position, RequestType } from 'vscode-languageclient';
import { DefaultClient, openFileVersions } from '../client';
import { processDelayedDidOpen } from '../extension';
import { ManualPromise } from '../../Utility/Async/manualPromise';
import { CppSettings } from '../settings';
interface GetInlayHintsParams {
uri: string;
interface FileData
{
version: number;
promise: ManualPromise<vscode.InlayHint[]>;
inlayHints: vscode.InlayHint[];
}
enum InlayHintKind {
Type = 0,
Parameter = 1,
}
interface CppInlayHint {
position: Position;
export interface CppInlayHint {
line: number;
character: number;
label: string;
inlayHintKind: InlayHintKind;
isValueRef: boolean;
@@ -28,81 +25,131 @@ interface CppInlayHint {
identifierLength: number;
}
interface GetInlayHintsResult {
fileVersion: number;
inlayHints: CppInlayHint[];
enum InlayHintKind {
Type = 0,
Parameter = 1,
}
type InlayHintsCacheEntry = {
FileVersion: number;
TypeHints: CppInlayHint[];
ParameterHints: CppInlayHint[];
};
const GetInlayHintsRequest: RequestType<GetInlayHintsParams, GetInlayHintsResult, void> =
new RequestType<GetInlayHintsParams, GetInlayHintsResult, void>('cpptools/getInlayHints');
export class InlayHintsProvider implements vscode.InlayHintsProvider {
private client: DefaultClient;
public onDidChangeInlayHintsEvent = new vscode.EventEmitter<void>();
public onDidChangeInlayHints?: vscode.Event<void>;
private cache: Map<string, InlayHintsCacheEntry> = new Map<string, InlayHintsCacheEntry>();
public onDidChangeInlayHints?: vscode.Event<void> = this.onDidChangeInlayHintsEvent.event;
private allFileData: Map<string, FileData> = new Map<string, FileData>();
constructor(client: DefaultClient) {
this.client = client;
this.onDidChangeInlayHints = this.onDidChangeInlayHintsEvent.event;
}
public async provideInlayHints(document: vscode.TextDocument, range: vscode.Range,
token: vscode.CancellationToken): Promise<vscode.InlayHint[] | undefined> {
await this.client.enqueue(() => processDelayedDidOpen(document));
const uriString: string = document.uri.toString();
// Get results from cache if available.
let cacheEntry: InlayHintsCacheEntry | undefined = this.cache.get(uriString);
if (cacheEntry?.FileVersion === document.version) {
return this.buildVSCodeHints(document.uri, cacheEntry);
public async provideInlayHints(document: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken): Promise<vscode.InlayHint[]> {
const uri: vscode.Uri = document.uri;
const uriString: string = uri.toString();
let fileData: FileData | undefined = this.allFileData.get(uriString);
if (fileData) {
if (fileData.promise.isCompleted) {
// Make sure file hasn't been changed since the last set of results.
// If a complete promise is present, there should also be a cache.
if (fileData.version === document.version) {
return fileData.promise;
}
} else {
// A new request requires a new ManualPromise, as each promise returned needs
// to be associated with the cancellation token provided at the time.
fileData.promise.reject(new vscode.CancellationError());
}
}
// Get new results from the language server
const params: GetInlayHintsParams = { uri: uriString };
const inlayHintsResult: GetInlayHintsResult = await this.client.languageClient.sendRequest(GetInlayHintsRequest, params, token);
if (token.isCancellationRequested || inlayHintsResult.inlayHints === undefined || inlayHintsResult.fileVersion !== openFileVersions.get(uriString)) {
throw new vscode.CancellationError();
fileData = {
version: document.version,
promise: new ManualPromise<vscode.InlayHint[]>(),
inlayHints: []
};
this.allFileData.set(uriString, fileData);
// Capture a local variable instead of referring to the member variable directly,
// to avoid race conditions where the member variable is changed before the
// cancallation token is triggered.
const currentPromise = fileData.promise;
token.onCancellationRequested(() => {
const fileData: FileData | undefined = this.allFileData.get(uriString);
if (fileData && currentPromise === fileData.promise) {
this.allFileData.delete(uriString);
currentPromise.reject(new vscode.CancellationError());
}
});
return currentPromise;
}
public deliverInlayHints(uriString: string, cppInlayHints: CppInlayHint[], startNewSet: boolean): void {
if (!startNewSet && cppInlayHints.length === 0) {
return;
}
cacheEntry = this.createCacheEntry(inlayHintsResult);
this.cache.set(uriString, cacheEntry);
return this.buildVSCodeHints(document.uri, cacheEntry);
const editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.find(e => e.document.uri.toString() === uriString);
if (!editor) {
this.allFileData.get(uriString)?.promise.resolve([]);
return;
}
}
// Use a lambda to remove ambiguity about whether fileData may be undefined.
const [fileData, wasNewPromiseCreated]: [FileData, boolean] = (() => {
let fileData = this.allFileData.get(uriString);
let newPromiseCreated = false;
if (!fileData) {
fileData = {
version: editor.document.version,
promise: new ManualPromise<vscode.InlayHint[]>(),
inlayHints: []
};
newPromiseCreated = true;
this.allFileData.set(uriString, fileData);
} else {
if (!fileData.promise.isPending) {
fileData.promise.reject(new vscode.CancellationError());
fileData.promise = new ManualPromise<vscode.InlayHint[]>();
newPromiseCreated = true;
}
if (fileData.version !== editor.document.version) {
fileData.version = editor.document.version;
fileData.inlayHints = [];
}
}
return [fileData, newPromiseCreated];
})();
if (startNewSet) {
fileData.inlayHints = [];
}
public invalidateFile(uri: string): void {
this.cache.delete(uri);
this.onDidChangeInlayHintsEvent.fire();
}
const typeHints: CppInlayHint[] = cppInlayHints.filter(h => h.inlayHintKind === InlayHintKind.Type);
const paramHints: CppInlayHint[] = cppInlayHints.filter(h => h.inlayHintKind === InlayHintKind.Parameter);
private buildVSCodeHints(uri: vscode.Uri, cacheEntry: InlayHintsCacheEntry): vscode.InlayHint[] {
let result: vscode.InlayHint[] = [];
const settings: CppSettings = new CppSettings(uri);
const settings: CppSettings = new CppSettings(vscode.Uri.parse(uriString));
if (settings.inlayHintsAutoDeclarationTypes) {
const resolvedTypeHints: vscode.InlayHint[] = this.resolveTypeHints(uri, cacheEntry.TypeHints);
result = result.concat(resolvedTypeHints);
const resolvedTypeHints: vscode.InlayHint[] = this.resolveTypeHints(settings, typeHints);
Array.prototype.push.apply(fileData.inlayHints, resolvedTypeHints);
}
if (settings.inlayHintsParameterNames || settings.inlayHintsReferenceOperator) {
const resolvedParameterHints: vscode.InlayHint[] = this.resolveParameterHints(uri, cacheEntry.ParameterHints);
result = result.concat(resolvedParameterHints);
const resolvedParameterHints: vscode.InlayHint[] = this.resolveParameterHints(settings, paramHints);
Array.prototype.push.apply(fileData.inlayHints, resolvedParameterHints);
}
fileData?.promise.resolve(fileData.inlayHints);
if (wasNewPromiseCreated) {
this.onDidChangeInlayHintsEvent.fire();
}
return result;
}
private resolveTypeHints(uri: vscode.Uri, hints: CppInlayHint[]): vscode.InlayHint[] {
public removeFile(uriString: string): void {
const fileData: FileData | undefined = this.allFileData.get(uriString);
if (!fileData) {
return;
}
if (fileData.promise.isPending) {
fileData.promise.reject(new vscode.CancellationError());
}
this.allFileData.delete(uriString);
}
private resolveTypeHints(settings: CppSettings, hints: CppInlayHint[]): vscode.InlayHint[] {
const resolvedHints: vscode.InlayHint[] = [];
const settings: CppSettings = new CppSettings(uri);
for (const hint of hints) {
const showOnLeft: boolean = settings.inlayHintsAutoDeclarationTypesShowOnLeft && hint.identifierLength > 0;
const inlayHint: vscode.InlayHint = new vscode.InlayHint(
new vscode.Position(hint.position.line, hint.position.character +
new vscode.Position(hint.line, hint.character +
(showOnLeft ? 0 : hint.identifierLength)),
showOnLeft ? hint.label : ": " + hint.label,
vscode.InlayHintKind.Type);
@@ -113,9 +160,8 @@ export class InlayHintsProvider implements vscode.InlayHintsProvider {
return resolvedHints;
}
private resolveParameterHints(uri: vscode.Uri, hints: CppInlayHint[]): vscode.InlayHint[] {
private resolveParameterHints(settings: CppSettings, hints: CppInlayHint[]): vscode.InlayHint[] {
const resolvedHints: vscode.InlayHint[] = [];
const settings: CppSettings = new CppSettings(uri);
for (const hint of hints) {
// Build parameter label based on settings.
let paramHintLabel: string = "";
@@ -144,7 +190,7 @@ export class InlayHintsProvider implements vscode.InlayHintsProvider {
}
const inlayHint: vscode.InlayHint = new vscode.InlayHint(
new vscode.Position(hint.position.line, hint.position.character),
new vscode.Position(hint.line, hint.character),
refOperatorString + paramHintLabel + ":",
vscode.InlayHintKind.Parameter);
inlayHint.paddingRight = true;
@@ -152,15 +198,4 @@ export class InlayHintsProvider implements vscode.InlayHintsProvider {
}
return resolvedHints;
}
private createCacheEntry(inlayHintsResults: GetInlayHintsResult): InlayHintsCacheEntry {
const typeHints: CppInlayHint[] = inlayHintsResults.inlayHints.filter(h => h.inlayHintKind === InlayHintKind.Type);
const paramHints: CppInlayHint[] = inlayHintsResults.inlayHints.filter(h => h.inlayHintKind === InlayHintKind.Parameter);
const cacheEntry: InlayHintsCacheEntry = {
FileVersion: inlayHintsResults.fileVersion,
TypeHints: typeHints,
ParameterHints: paramHints
};
return cacheEntry;
}
}
@@ -3,55 +3,127 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { DefaultClient, GetSemanticTokensParams, GetSemanticTokensRequest, GetSemanticTokensResult, openFileVersions, semanticTokensLegend } from '../client';
import { processDelayedDidOpen } from '../extension';
import { ManualPromise } from '../../Utility/Async/manualPromise';
interface FileData
{
version: number;
promise: ManualPromise<vscode.SemanticTokens>;
tokenBuilder: vscode.SemanticTokensBuilder;
}
export interface SemanticToken {
line: number;
character: number;
length: number;
type: number;
modifiers?: number;
}
export class SemanticTokensProvider implements vscode.DocumentSemanticTokensProvider {
private client: DefaultClient;
public onDidChangeSemanticTokensEvent = new vscode.EventEmitter<void>();
public onDidChangeSemanticTokens?: vscode.Event<void>;
private tokenCaches: Map<string, [number, vscode.SemanticTokens]> = new Map<string, [number, vscode.SemanticTokens]>();
constructor(client: DefaultClient) {
this.client = client;
this.onDidChangeSemanticTokens = this.onDidChangeSemanticTokensEvent.event;
}
public onDidChangeSemanticTokens?: vscode.Event<void> = this.onDidChangeSemanticTokensEvent.event;
private allFileData: Map<string, FileData> = new Map<string, FileData>();
public async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.SemanticTokens> {
const editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.find(e => e.document === document);
const uri: vscode.Uri = document.uri;
const uriString: string = uri.toString();
let fileData: FileData | undefined = this.allFileData.get(uriString);
if (fileData) {
if (fileData.promise.isCompleted) {
// Make sure file hasn't been changed since the last set of results.
// If a complete promise is present, there should also be a cache.
if (fileData.version === document.version) {
return fileData.promise;
}
} else {
// A new request requires a new ManualPromise, as each promise returned needs
// to be associated with the cancellation token provided at the time.
fileData.promise.reject(new vscode.CancellationError());
}
}
fileData = {
version: document.version,
promise: new ManualPromise<vscode.SemanticTokens>(),
tokenBuilder: new vscode.SemanticTokensBuilder()
};
this.allFileData.set(uriString, fileData);
// Capture a local variable instead of referring to the member variable directly,
// to avoid race conditions where the member variable is changed before the
// cancallation token is triggered.
const currentPromise = fileData.promise;
token.onCancellationRequested(() => {
const fileData: FileData | undefined = this.allFileData.get(uriString);
if (fileData && currentPromise === fileData.promise) {
this.allFileData.delete(uriString);
currentPromise.reject(new vscode.CancellationError());
}
});
return currentPromise;
}
public deliverTokens(uriString: string, semanticTokens: SemanticToken[], startNewSet: boolean): void {
if (!startNewSet && semanticTokens.length === 0) {
return;
}
const editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.find(e => e.document.uri.toString() === uriString);
if (!editor) {
// Don't provide document semantic tokens for files that aren't visible,
// which prevents launching a lot of IntelliSense processes from a find/replace.
const builder: vscode.SemanticTokensBuilder = new vscode.SemanticTokensBuilder();
const tokens: vscode.SemanticTokens = builder.build();
return tokens;
this.allFileData.get(uriString)?.promise.resolve(tokens);
return;
}
await this.client.enqueue(() => processDelayedDidOpen(document));
const uriString: string = document.uri.toString();
// First check the semantic token cache to see if we already have results for that file and version
const cache: [number, vscode.SemanticTokens] | undefined = this.tokenCaches.get(uriString);
if (cache && cache[0] === document.version) {
return cache[1];
// Use a lambda to remove ambiguity about whether fileData may be undefined.
const [fileData, wasNewPromiseCreated]: [FileData, boolean] = (() => {
let fileData = this.allFileData.get(uriString);
let newPromiseCreated = false;
if (!fileData) {
fileData = {
version: editor.document.version,
promise: new ManualPromise<vscode.SemanticTokens>(),
tokenBuilder: new vscode.SemanticTokensBuilder()
};
newPromiseCreated = true;
this.allFileData.set(uriString, fileData);
} else {
if (!fileData.promise.isPending) {
fileData.promise.reject(new vscode.CancellationError());
fileData.promise = new ManualPromise<vscode.SemanticTokens>();
newPromiseCreated = true;
}
if (fileData.version !== editor.document.version) {
fileData.version = editor.document.version;
fileData.tokenBuilder = new vscode.SemanticTokensBuilder();
}
}
return [fileData, newPromiseCreated];
})();
if (startNewSet) {
fileData.tokenBuilder = new vscode.SemanticTokensBuilder();
}
const params: GetSemanticTokensParams = {
uri: uriString
};
const tokensResult: GetSemanticTokensResult = await this.client.languageClient.sendRequest(GetSemanticTokensRequest, params, token);
if (token.isCancellationRequested || tokensResult.tokens === undefined || tokensResult.fileVersion !== openFileVersions.get(uriString)) {
throw new vscode.CancellationError();
}
const builder: vscode.SemanticTokensBuilder = new vscode.SemanticTokensBuilder(semanticTokensLegend);
tokensResult.tokens.forEach((semanticToken) => {
builder.push(semanticToken.line, semanticToken.character, semanticToken.length, semanticToken.type, semanticToken.modifiers);
semanticTokens.forEach((semanticToken) => {
fileData.tokenBuilder.push(semanticToken.line, semanticToken.character, semanticToken.length, semanticToken.type, semanticToken.modifiers);
});
const tokens: vscode.SemanticTokens = builder.build();
this.tokenCaches.set(uriString, [tokensResult.fileVersion, tokens]);
return tokens;
fileData?.promise.resolve(fileData.tokenBuilder.build());
if (wasNewPromiseCreated) {
this.onDidChangeSemanticTokensEvent.fire();
}
}
public invalidateFile(uri: string): void {
this.tokenCaches.delete(uri);
this.onDidChangeSemanticTokensEvent.fire();
public removeFile(uriString: string): void {
const fileData: FileData | undefined = this.allFileData.get(uriString);
if (!fileData) {
return;
}
if (fileData.promise.isPending) {
fileData.promise.reject(new vscode.CancellationError());
}
this.allFileData.delete(uriString);
}
}
+251 -160
View File
@@ -15,10 +15,10 @@ import { DocumentRangeFormattingEditProvider } from './Providers/documentRangeFo
import { DocumentSymbolProvider } from './Providers/documentSymbolProvider';
import { FindAllReferencesProvider } from './Providers/findAllReferencesProvider';
import { FoldingRangeProvider } from './Providers/foldingRangeProvider';
import { InlayHintsProvider } from './Providers/inlayHintProvider';
import { CppInlayHint, InlayHintsProvider } from './Providers/inlayHintProvider';
import { OnTypeFormattingEditProvider } from './Providers/onTypeFormattingEditProvider';
import { RenameProvider } from './Providers/renameProvider';
import { SemanticTokensProvider } from './Providers/semanticTokensProvider';
import { SemanticToken, SemanticTokensProvider } from './Providers/semanticTokensProvider';
import { WorkspaceSymbolProvider } from './Providers/workspaceSymbolProvider';
// End provider imports
@@ -61,7 +61,7 @@ import * as refs from './references';
import { CppSettings, OtherSettings, SettingsParams, WorkspaceFolderSettingsParams, getEditorConfigSettings } from './settings';
import { SettingsTracker } from './settingsTracker';
import { ConfigurationType, LanguageStatusUI, getUI } from './ui';
import { handleChangedFromCppToC, makeVscodeLocation, makeVscodeRange } from './utils';
import { handleChangedFromCppToC, makeLspRange, makeVscodeLocation, makeVscodeRange } from './utils';
import minimatch = require("minimatch");
function deepCopy(obj: any) {
@@ -137,34 +137,6 @@ function showMessageWindow(params: ShowMessageWindowParams): void {
}
}
function publishIntelliSenseDiagnostics(params: PublishIntelliSenseDiagnosticsParams): void {
if (!diagnosticsCollectionIntelliSense) {
diagnosticsCollectionIntelliSense = vscode.languages.createDiagnosticCollection(configPrefix + "IntelliSense");
}
// Convert from our Diagnostic objects to vscode Diagnostic objects
const diagnosticsIntelliSense: vscode.Diagnostic[] = [];
params.diagnostics.forEach((d) => {
const message: string = getLocalizedString(d.localizeStringParams);
const diagnostic: vscode.Diagnostic = new vscode.Diagnostic(makeVscodeRange(d.range), message, d.severity);
diagnostic.code = d.code;
diagnostic.source = CppSourceStr;
if (d.relatedInformation) {
diagnostic.relatedInformation = [];
for (const info of d.relatedInformation) {
diagnostic.relatedInformation.push(new vscode.DiagnosticRelatedInformation(makeVscodeLocation(info.location), info.message));
}
}
diagnosticsIntelliSense.push(diagnostic);
});
const realUri: vscode.Uri = vscode.Uri.parse(params.uri);
diagnosticsCollectionIntelliSense.set(realUri, diagnosticsIntelliSense);
clients.timeTelemetryCollector.setUpdateRangeTime(realUri);
}
function publishRefactorDiagnostics(params: PublishRefactorDiagnosticsParams): void {
if (!diagnosticsCollectionRefactor) {
diagnosticsCollectionRefactor = vscode.languages.createDiagnosticCollection(configPrefix + "Refactor");
@@ -251,12 +223,6 @@ interface DecorationRangesPair {
ranges: vscode.Range[];
}
interface InactiveRegionParams {
uri: string;
fileVersion: number;
regions: InputRegion[];
}
interface InternalSourceFileConfiguration extends SourceFileConfiguration {
compilerArgsLegacy?: string[];
}
@@ -322,11 +288,6 @@ interface RefactorDiagnostic {
relatedInformation?: RefactorDiagnosticRelatedInformation[];
}
interface PublishIntelliSenseDiagnosticsParams {
uri: string;
diagnostics: IntelliSenseDiagnostic[];
}
interface PublishRefactorDiagnosticsParams {
uri: string;
diagnostics: RefactorDiagnostic[];
@@ -420,21 +381,18 @@ export interface GetFoldingRangesResult {
ranges: CppFoldingRange[];
}
export interface GetSemanticTokensParams {
export interface IntelliSenseResult {
uri: string;
}
interface SemanticToken {
line: number;
character: number;
length: number;
type: number;
modifiers?: number;
}
export interface GetSemanticTokensResult {
fileVersion: number;
tokens: SemanticToken[];
diagnostics: IntelliSenseDiagnostic[];
inactiveRegions: InputRegion[];
semanticTokens: SemanticToken[];
inlayHints: CppInlayHint[];
clearExistingDiagnostics: boolean;
clearExistingInactiveRegions: boolean;
clearExistingSemanticTokens: boolean;
clearExistingInlayHint: boolean;
isCompletePass: boolean;
}
enum SemanticTokenTypes {
@@ -512,7 +470,7 @@ export interface DoxygenCodeActionCommandArguments {
}
interface SetTemporaryTextDocumentLanguageParams {
path: string;
uri: string;
isC: boolean;
isCuda: boolean;
}
@@ -532,10 +490,6 @@ interface FinishedRequestCustomConfigParams {
uri: string;
}
interface IntervalTimerParams {
freeMemory: number;
}
export interface TextDocumentWillSaveParams {
textDocument: TextDocumentIdentifier;
reason: vscode.TextDocumentSaveReason;
@@ -551,7 +505,6 @@ interface CppInitializationParams {
cacheStoragePath: string;
workspaceStoragePath: string;
databaseStoragePath: string;
freeMemory: number;
vcpkgRoot: string;
intelliSenseCacheDisabled: boolean;
caseSensitiveFileSupport: boolean;
@@ -566,6 +519,22 @@ interface TagParseStatus {
isPaused: boolean;
}
interface DidChangeVisibleTextEditorsParams {
activeUri?: string;
activeSelection?: Range;
visibleRanges?: { [uri: string]: Range[] };
}
interface DidChangeTextEditorVisibleRangesParams {
uri: string;
visibleRanges: Range[];
}
interface DidChangeActiveEditorParams {
uri?: string;
selection?: Range;
}
// Requests
const InitializationRequest: RequestType<CppInitializationParams, void, void> = new RequestType<CppInitializationParams, void, void>('cpptools/initialize');
const QueryCompilerDefaultsRequest: RequestType<QueryDefaultCompilerParams, configs.CompilerDefaults, void> = new RequestType<QueryDefaultCompilerParams, configs.CompilerDefaults, void>('cpptools/queryCompilerDefaults');
@@ -575,7 +544,6 @@ const GetDiagnosticsRequest: RequestType<void, GetDiagnosticsResult, void> = new
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');
export const GetFoldingRangesRequest: RequestType<GetFoldingRangesParams, GetFoldingRangesResult, void> = new RequestType<GetFoldingRangesParams, GetFoldingRangesResult, void>('cpptools/getFoldingRanges');
export const GetSemanticTokensRequest: RequestType<GetSemanticTokensParams, GetSemanticTokensResult, void> = new RequestType<GetSemanticTokensParams, GetSemanticTokensResult, void>('cpptools/getSemanticTokens');
export const FormatDocumentRequest: RequestType<FormatParams, FormatResult, void> = new RequestType<FormatParams, FormatResult, void>('cpptools/formatDocument');
export const FormatRangeRequest: RequestType<FormatParams, FormatResult, void> = new RequestType<FormatParams, FormatResult, void>('cpptools/formatRange');
export const FormatOnTypeRequest: RequestType<FormatParams, FormatResult, void> = new RequestType<FormatParams, FormatResult, void>('cpptools/formatOnType');
@@ -593,12 +561,12 @@ const FileDeletedNotification: NotificationType<FileChangedParams> = new Notific
const ResetDatabaseNotification: NotificationType<void> = new NotificationType<void>('cpptools/resetDatabase');
const PauseParsingNotification: NotificationType<void> = new NotificationType<void>('cpptools/pauseParsing');
const ResumeParsingNotification: NotificationType<void> = new NotificationType<void>('cpptools/resumeParsing');
const ActiveDocumentChangeNotification: NotificationType<TextDocumentIdentifier> = new NotificationType<TextDocumentIdentifier>('cpptools/activeDocumentChange');
const DidChangeActiveEditorNotification: NotificationType<DidChangeActiveEditorParams> = new NotificationType<DidChangeActiveEditorParams>('cpptools/didChangeActiveEditor');
const RestartIntelliSenseForFileNotification: NotificationType<TextDocumentIdentifier> = new NotificationType<TextDocumentIdentifier>('cpptools/restartIntelliSenseForFile');
const TextEditorSelectionChangeNotification: NotificationType<Range> = new NotificationType<Range>('cpptools/textEditorSelectionChange');
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');
const IntervalTimerNotification: NotificationType<IntervalTimerParams> = new NotificationType<IntervalTimerParams>('cpptools/onIntervalTimer');
const IntervalTimerNotification: NotificationType<void> = new NotificationType<void>('cpptools/onIntervalTimer');
const CustomConfigurationNotification: NotificationType<CustomConfigurationParams> = new NotificationType<CustomConfigurationParams>('cpptools/didChangeCustomConfiguration');
const CustomBrowseConfigurationNotification: NotificationType<CustomBrowseConfigurationParams> = new NotificationType<CustomBrowseConfigurationParams>('cpptools/didChangeCustomBrowseConfiguration');
const ClearCustomConfigurationsNotification: NotificationType<WorkspaceFolderParams> = new NotificationType<WorkspaceFolderParams>('cpptools/clearCustomConfigurations');
@@ -607,6 +575,8 @@ const PreviewReferencesNotification: NotificationType<void> = new NotificationTy
const RescanFolderNotification: NotificationType<void> = new NotificationType<void>('cpptools/rescanFolder');
const FinishedRequestCustomConfig: NotificationType<FinishedRequestCustomConfigParams> = new NotificationType<FinishedRequestCustomConfigParams>('cpptools/finishedRequestCustomConfig');
const DidChangeSettingsNotification: NotificationType<SettingsParams> = new NotificationType<SettingsParams>('cpptools/didChangeSettings');
const DidChangeVisibleTextEditorsNotification: NotificationType<DidChangeVisibleTextEditorsParams> = new NotificationType<DidChangeVisibleTextEditorsParams>('cpptools/didChangeVisibleTextEditors');
const DidChangeTextEditorVisibleRangesNotification: NotificationType<DidChangeTextEditorVisibleRangesParams> = new NotificationType<DidChangeTextEditorVisibleRangesParams>('cpptools/didChangeTextEditorVisibleRanges');
const CodeAnalysisNotification: NotificationType<CodeAnalysisParams> = new NotificationType<CodeAnalysisParams>('cpptools/runCodeAnalysis');
const PauseCodeAnalysisNotification: NotificationType<void> = new NotificationType<void>('cpptools/pauseCodeAnalysis');
@@ -622,24 +592,21 @@ const ReportTagParseStatusNotification: NotificationType<TagParseStatus> = new N
const ReportStatusNotification: NotificationType<ReportStatusNotificationBody> = new NotificationType<ReportStatusNotificationBody>('cpptools/reportStatus');
const DebugProtocolNotification: NotificationType<DebugProtocolParams> = new NotificationType<DebugProtocolParams>('cpptools/debugProtocol');
const DebugLogNotification: NotificationType<LocalizeStringParams> = new NotificationType<LocalizeStringParams>('cpptools/debugLog');
const InactiveRegionNotification: NotificationType<InactiveRegionParams> = new NotificationType<InactiveRegionParams>('cpptools/inactiveRegions');
const CompileCommandsPathsNotification: NotificationType<CompileCommandsPaths> = new NotificationType<CompileCommandsPaths>('cpptools/compileCommandsPaths');
const ReferencesNotification: NotificationType<refs.ReferencesResult> = new NotificationType<refs.ReferencesResult>('cpptools/references');
const ReportReferencesProgressNotification: NotificationType<refs.ReportReferencesProgressNotification> = new NotificationType<refs.ReportReferencesProgressNotification>('cpptools/reportReferencesProgress');
const RequestCustomConfig: NotificationType<string> = new NotificationType<string>('cpptools/requestCustomConfig');
const PublishIntelliSenseDiagnosticsNotification: NotificationType<PublishIntelliSenseDiagnosticsParams> = new NotificationType<PublishIntelliSenseDiagnosticsParams>('cpptools/publishIntelliSenseDiagnostics');
const PublishRefactorDiagnosticsNotification: NotificationType<PublishRefactorDiagnosticsParams> = new NotificationType<PublishRefactorDiagnosticsParams>('cpptools/publishRefactorDiagnostics');
const ShowMessageWindowNotification: NotificationType<ShowMessageWindowParams> = new NotificationType<ShowMessageWindowParams>('cpptools/showMessageWindow');
const ShowWarningNotification: NotificationType<ShowWarningParams> = new NotificationType<ShowWarningParams>('cpptools/showWarning');
const ReportTextDocumentLanguage: NotificationType<string> = new NotificationType<string>('cpptools/reportTextDocumentLanguage');
const SemanticTokensChanged: NotificationType<string> = new NotificationType<string>('cpptools/semanticTokensChanged');
const InlayHintsChanged: NotificationType<string> = new NotificationType<string>('cpptools/inlayHintsChanged');
const IntelliSenseSetupNotification: NotificationType<IntelliSenseSetup> = new NotificationType<IntelliSenseSetup>('cpptools/IntelliSenseSetup');
const SetTemporaryTextDocumentLanguageNotification: NotificationType<SetTemporaryTextDocumentLanguageParams> = new NotificationType<SetTemporaryTextDocumentLanguageParams>('cpptools/setTemporaryTextDocumentLanguage');
const ReportCodeAnalysisProcessedNotification: NotificationType<number> = new NotificationType<number>('cpptools/reportCodeAnalysisProcessed');
const ReportCodeAnalysisTotalNotification: NotificationType<number> = new NotificationType<number>('cpptools/reportCodeAnalysisTotal');
const DoxygenCommentGeneratedNotification: NotificationType<GenerateDoxygenCommentResult> = new NotificationType<GenerateDoxygenCommentResult>('cpptools/insertDoxygenComment');
const CanceledReferencesNotification: NotificationType<void> = new NotificationType<void>('cpptools/canceledReferences');
const IntelliSenseResultNotification: NotificationType<IntelliSenseResult> = new NotificationType<IntelliSenseResult>('cpptools/intelliSenseResult');
let failureMessageShown: boolean = false;
@@ -744,11 +711,12 @@ export interface Client {
RootUri?: vscode.Uri;
RootFolder?: vscode.WorkspaceFolder;
Name: string;
TrackedDocuments: Set<vscode.TextDocument>;
TrackedDocuments: Map<string, vscode.TextDocument>;
onDidChangeSettings(event: vscode.ConfigurationChangeEvent): Promise<Record<string, string>>;
onDidOpenTextDocument(document: vscode.TextDocument): void;
onDidCloseTextDocument(document: vscode.TextDocument): void;
onDidChangeVisibleTextEditor(editor: vscode.TextEditor): void;
onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]): Promise<void>;
onDidChangeTextEditorVisibleRanges(uri: vscode.Uri): Promise<void>;
onDidChangeTextDocument(textDocumentChangeEvent: vscode.TextDocumentChangeEvent): void;
onRegisterCustomConfigurationProvider(provider: CustomConfigurationProvider1): Thenable<void>;
updateCustomConfigurations(requestingProvider?: CustomConfigurationProvider1): Thenable<void>;
@@ -764,10 +732,11 @@ export interface Client {
getVcpkgEnabled(): Thenable<boolean>;
getCurrentCompilerPathAndArgs(): Thenable<util.CompilerPathAndArgs | undefined>;
getKnownCompilers(): Thenable<configs.KnownCompiler[] | undefined>;
takeOwnership(document: vscode.TextDocument): Promise<void>;
takeOwnership(document: vscode.TextDocument): void;
sendDidOpen(document: vscode.TextDocument): Promise<void>;
requestSwitchHeaderSource(rootUri: vscode.Uri, fileName: string): Thenable<string>;
activeDocumentChanged(document: vscode.TextDocument): Promise<void>;
updateActiveDocumentTextOptions(): void;
didChangeActiveEditor(editor?: vscode.TextEditor, selection?: Range): Promise<void>;
restartIntelliSenseForFile(document: vscode.TextDocument): Promise<void>;
activate(): void;
selectionChanged(selection: Range): void;
@@ -834,7 +803,7 @@ export class DefaultClient implements Client {
private rootFolder?: vscode.WorkspaceFolder;
private rootRealPath: string;
private workspaceStoragePath: string;
private trackedDocuments = new Set<vscode.TextDocument>();
private trackedDocuments = new Map<string, vscode.TextDocument>();
private isSupported: boolean = true;
private inactiveRegionsDecorations = new Map<string, DecorationRangesPair>();
private settingsTracker: SettingsTracker;
@@ -901,7 +870,7 @@ export class DefaultClient implements Client {
public get Name(): string {
return this.getName(this.rootFolder);
}
public get TrackedDocuments(): Set<vscode.TextDocument> {
public get TrackedDocuments(): Map<string, vscode.TextDocument> {
return this.trackedDocuments;
}
public get IsTagParsing(): boolean {
@@ -1262,7 +1231,7 @@ export class DefaultClient implements Client {
// e.g. prevents empty c_cpp_properties.json from generation.
this.registerFileWatcher();
initializedClientCount = 0;
this.inlayHintsProvider = new InlayHintsProvider(this);
this.inlayHintsProvider = new InlayHintsProvider();
this.disposables.push(vscode.languages.registerInlayHintsProvider(util.documentSelector, this.inlayHintsProvider));
this.disposables.push(vscode.languages.registerRenameProvider(util.documentSelector, new RenameProvider(this)));
@@ -1283,12 +1252,14 @@ export class DefaultClient implements Client {
const settings: CppSettings = new CppSettings();
if (settings.enhancedColorization && semanticTokensLegend) {
this.semanticTokensProvider = new SemanticTokensProvider(this);
this.semanticTokensProvider = new SemanticTokensProvider();
this.semanticTokensProviderDisposable = vscode.languages.registerDocumentSemanticTokensProvider(util.documentSelector, this.semanticTokensProvider, semanticTokensLegend);
}
// Listen for messages from the language server.
this.registerNotifications();
}
// update all client configurations
this.configuration.setupConfigurations();
initializedClientCount++;
@@ -1539,7 +1510,6 @@ export class DefaultClient implements Client {
databaseStoragePath: databaseStoragePath,
workspaceStoragePath: this.workspaceStoragePath,
cacheStoragePath: cacheStoragePath,
freeMemory: Math.floor(os.freemem() / 1048576),
vcpkgRoot: util.getVcpkgRoot(),
intelliSenseCacheDisabled: intelliSenseCacheDisabled,
caseSensitiveFileSupport: workspaceSettings.caseSensitiveFileSupport,
@@ -1639,7 +1609,7 @@ export class DefaultClient implements Client {
const settings: CppSettings = new CppSettings();
if (changedSettings.enhancedColorization) {
if (settings.enhancedColorization && semanticTokensLegend) {
this.semanticTokensProvider = new SemanticTokensProvider(this);
this.semanticTokensProvider = new SemanticTokensProvider();
this.semanticTokensProviderDisposable = vscode.languages.registerDocumentSemanticTokensProvider(util.documentSelector, this.semanticTokensProvider, semanticTokensLegend);
} else if (this.semanticTokensProviderDisposable) {
this.semanticTokensProviderDisposable.dispose();
@@ -1670,15 +1640,65 @@ export class DefaultClient implements Client {
return changedSettings;
}
public onDidChangeVisibleTextEditor(editor: vscode.TextEditor): void {
const settings: CppSettings = new CppSettings(this.RootUri);
if (settings.dimInactiveRegions) {
// Apply text decorations to inactive regions
const valuePair: DecorationRangesPair | undefined = this.inactiveRegionsDecorations.get(editor.document.uri.toString());
if (valuePair) {
editor.setDecorations(valuePair.decoration, valuePair.ranges); // VSCode clears the decorations when the text editor becomes invisible
private prepareVisibleRanges(editors: readonly vscode.TextEditor[]): { [uri: string]: Range[] } {
const visibleRanges: { [uri: string]: Range[] } = {};
editors.forEach(editor => {
// Use a map, to account for multiple editors for the same file.
// First, we just concat all ranges for the same file.
const uri: string = editor.document.uri.toString();
if (!visibleRanges[uri]) {
visibleRanges[uri] = [];
}
visibleRanges[uri] = visibleRanges[uri].concat(editor.visibleRanges.map(makeLspRange));
});
// We may need to merge visible ranges, if there are multiple editors for the same file,
// and some of the ranges overlap.
Object.keys(visibleRanges).forEach(uri => {
visibleRanges[uri] = util.mergeOverlappingRanges(visibleRanges[uri]);
});
return visibleRanges;
}
// Handles changes to visible files/ranges, changes to current selection/position,
// and changes to the active text editor. Should only be called on the primary client.
public async onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]): Promise<void> {
const params: DidChangeVisibleTextEditorsParams = {
visibleRanges: this.prepareVisibleRanges(editors)
};
if (vscode.window.activeTextEditor) {
if (util.isCpp(vscode.window.activeTextEditor.document)) {
params.activeUri = vscode.window.activeTextEditor.document.uri.toString();
params.activeSelection = makeLspRange(vscode.window.activeTextEditor.selection);
}
}
await this.languageClient.sendNotification(DidChangeVisibleTextEditorsNotification, params);
}
public async onDidChangeTextEditorVisibleRanges(uri: vscode.Uri): Promise<void> {
// VS Code will notify us of a particular editor, but same file may be open in
// multiple editors, so we coalesc those visible ranges.
const editors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(editor => editor.document.uri === uri);
let visibleRanges: Range[] = [];
if (editors.length === 1) {
visibleRanges = editors[0].visibleRanges.map(makeLspRange);
} else {
editors.forEach(editor => {
// Use a map, to account for multiple editors for the same file.
// First, we just concat all ranges for the same file.
visibleRanges = visibleRanges.concat(editor.visibleRanges.map(makeLspRange));
});
}
const params: DidChangeTextEditorVisibleRangesParams = {
uri: uri.toString(),
visibleRanges
};
await this.languageClient.sendNotification(DidChangeTextEditorVisibleRangesNotification, params);
}
public onDidChangeTextDocument(textDocumentChangeEvent: vscode.TextDocumentChangeEvent): void {
@@ -1710,10 +1730,14 @@ export class DefaultClient implements Client {
public onDidCloseTextDocument(document: vscode.TextDocument): void {
const uri: string = document.uri.toString();
if (this.semanticTokensProvider) {
this.semanticTokensProvider.invalidateFile(uri);
this.semanticTokensProvider.removeFile(uri);
}
if (this.inlayHintsProvider) {
this.inlayHintsProvider.invalidateFile(uri);
this.inlayHintsProvider.removeFile(uri);
}
this.inactiveRegionsDecorations.delete(uri);
if (diagnosticsCollectionIntelliSense) {
diagnosticsCollectionIntelliSense.delete(document.uri);
}
openFileVersions.delete(uri);
}
@@ -1790,11 +1814,9 @@ export class DefaultClient implements Client {
return;
}
await this.clearCustomConfigurations();
await Promise.all([
this.clearCustomConfigurations(),
this.handleRemoveAllCodeAnalysisProblems()]);
await Promise.all([
...[...this.trackedDocuments].map(document => this.provideCustomConfiguration(document.uri, undefined, true))
...[...this.trackedDocuments].map(([_uri, document]) => this.provideCustomConfiguration(document.uri, undefined, true))
]);
}
@@ -2120,14 +2142,11 @@ export class DefaultClient implements Client {
* that it knows about the file, as well as adding it to this client's set of
* tracked documents.
*/
public async takeOwnership(document: vscode.TextDocument): Promise<void> {
this.trackedDocuments.add(document);
this.updateActiveDocumentTextOptions();
// in case the client is recreated, wait for the isStarted to finish.
await DefaultClient.isStarted;
return this.sendDidOpen(document);
public takeOwnership(document: vscode.TextDocument): void {
this.trackedDocuments.set(document.uri.toString(), document);
}
// Only used in crash recovery. Otherwise, VS Code sends didOpen directly to native process (through the protocolFilter).
public async sendDidOpen(document: vscode.TextDocument): Promise<void> {
const params: DidOpenTextDocumentParams = {
textDocument: {
@@ -2137,6 +2156,7 @@ export class DefaultClient implements Client {
text: document.getText()
}
};
await this.ready;
await this.languageClient.sendNotification(DidOpenNotification, params);
}
@@ -2256,7 +2276,6 @@ export class DefaultClient implements Client {
this.languageClient.onNotification(LogTelemetryNotification, logTelemetry);
this.languageClient.onNotification(ReportStatusNotification, (e) => void this.updateStatus(e));
this.languageClient.onNotification(ReportTagParseStatusNotification, (e) => this.updateTagParseStatus(e));
this.languageClient.onNotification(InactiveRegionNotification, (e) => this.updateInactiveRegions(e));
this.languageClient.onNotification(CompileCommandsPathsNotification, (e) => void this.promptCompileCommands(e));
this.languageClient.onNotification(ReferencesNotification, (e) => this.processReferencesPreview(e));
this.languageClient.onNotification(ReportReferencesProgressNotification, (e) => this.handleReferencesProgress(e));
@@ -2267,14 +2286,12 @@ export class DefaultClient implements Client {
void defaultClient.handleRequestCustomConfig(requestFile);
}
});
this.languageClient.onNotification(PublishIntelliSenseDiagnosticsNotification, publishIntelliSenseDiagnostics);
this.languageClient.onNotification(IntelliSenseResultNotification, (e) => this.handleIntelliSenseResult(e));
this.languageClient.onNotification(PublishRefactorDiagnosticsNotification, publishRefactorDiagnostics);
RegisterCodeAnalysisNotifications(this.languageClient);
this.languageClient.onNotification(ShowMessageWindowNotification, showMessageWindow);
this.languageClient.onNotification(ShowWarningNotification, showWarning);
this.languageClient.onNotification(ReportTextDocumentLanguage, (e) => this.setTextDocumentLanguage(e));
this.languageClient.onNotification(SemanticTokensChanged, (e) => this.semanticTokensProvider?.invalidateFile(e));
this.languageClient.onNotification(InlayHintsChanged, (e) => this.inlayHintsProvider?.invalidateFile(e));
this.languageClient.onNotification(IntelliSenseSetupNotification, (e) => this.logIntelliSenseSetupTime(e));
this.languageClient.onNotification(SetTemporaryTextDocumentLanguageNotification, (e) => void this.setTemporaryTextDocumentLanguage(e));
this.languageClient.onNotification(ReportCodeAnalysisProcessedNotification, (e) => this.updateCodeAnalysisProcessed(e));
@@ -2283,6 +2300,60 @@ export class DefaultClient implements Client {
this.languageClient.onNotification(CanceledReferencesNotification, this.serverCanceledReferences);
}
private handleIntelliSenseResult(intelliseSenseResult: IntelliSenseResult): void {
const fileVersion: number | undefined = openFileVersions.get(intelliseSenseResult.uri);
if (fileVersion !== undefined && fileVersion !== intelliseSenseResult.fileVersion) {
return;
}
if (this.semanticTokensProvider) {
this.semanticTokensProvider.deliverTokens(intelliseSenseResult.uri, intelliseSenseResult.semanticTokens, intelliseSenseResult.clearExistingSemanticTokens);
}
if (this.inlayHintsProvider) {
this.inlayHintsProvider.deliverInlayHints(intelliseSenseResult.uri, intelliseSenseResult.inlayHints, intelliseSenseResult.clearExistingInlayHint);
}
this.updateInactiveRegions(intelliseSenseResult.uri, intelliseSenseResult.inactiveRegions, intelliseSenseResult.clearExistingInactiveRegions, intelliseSenseResult.isCompletePass);
this.updateSquiggles(intelliseSenseResult.uri, intelliseSenseResult.diagnostics, intelliseSenseResult.clearExistingDiagnostics);
}
private updateSquiggles(uriString: string, diagnostics: IntelliSenseDiagnostic[], startNewSet: boolean): void {
if (!diagnosticsCollectionIntelliSense) {
diagnosticsCollectionIntelliSense = vscode.languages.createDiagnosticCollection(configPrefix + "IntelliSense");
}
// Convert from our Diagnostic objects to vscode Diagnostic objects
const diagnosticsIntelliSense: vscode.Diagnostic[] = [];
diagnostics.forEach((d) => {
const message: string = getLocalizedString(d.localizeStringParams);
const diagnostic: vscode.Diagnostic = new vscode.Diagnostic(makeVscodeRange(d.range), message, d.severity);
diagnostic.code = d.code;
diagnostic.source = CppSourceStr;
if (d.relatedInformation) {
diagnostic.relatedInformation = [];
for (const info of d.relatedInformation) {
diagnostic.relatedInformation.push(new vscode.DiagnosticRelatedInformation(makeVscodeLocation(info.location), info.message));
}
}
diagnosticsIntelliSense.push(diagnostic);
});
const realUri: vscode.Uri = vscode.Uri.parse(uriString);
if (!startNewSet) {
const existingDiagnostics: readonly vscode.Diagnostic[] | undefined = diagnosticsCollectionIntelliSense.get(realUri);
if (existingDiagnostics) {
// Note: The spread operator puts every element on the stack, so it should be avoided for large arrays.
Array.prototype.push.apply(diagnosticsIntelliSense, existingDiagnostics as any[]);
}
}
diagnosticsCollectionIntelliSense.set(realUri, diagnosticsIntelliSense);
clients.timeTelemetryCollector.setUpdateRangeTime(realUri);
}
private setTextDocumentLanguage(languageStr: string): void {
const cppSettings: CppSettings = new CppSettings();
if (cppSettings.autoAddFileAssociations) {
@@ -2295,7 +2366,9 @@ export class DefaultClient implements Client {
private async setTemporaryTextDocumentLanguage(params: SetTemporaryTextDocumentLanguageParams): Promise<void> {
const languageId: string = params.isC ? "c" : params.isCuda ? "cuda-cpp" : "cpp";
const document: vscode.TextDocument = await vscode.workspace.openTextDocument(params.path);
const uri: vscode.Uri = vscode.Uri.parse(params.uri);
const client: Client = clients.getClientFor(uri);
const document: vscode.TextDocument | undefined = client.TrackedDocuments.get(params.uri);
if (!!document && document.languageId !== languageId) {
if (document.languageId === "cpp" && languageId === "c") {
handleChangedFromCppToC(document);
@@ -2504,44 +2577,49 @@ export class DefaultClient implements Client {
this.model.isParsingWorkspacePaused.Value = tagParseStatus.isPaused;
}
private updateInactiveRegions(params: InactiveRegionParams): void {
const settings: CppSettings = new CppSettings(this.RootUri);
const opacity: number | undefined = settings.inactiveRegionOpacity;
if (opacity !== null && opacity !== undefined) {
const decoration: vscode.TextEditorDecorationType = vscode.window.createTextEditorDecorationType({
opacity: opacity.toString(),
backgroundColor: settings.inactiveRegionBackgroundColor,
color: settings.inactiveRegionForegroundColor,
rangeBehavior: vscode.DecorationRangeBehavior.OpenOpen
});
// We must convert to vscode.Ranges in order to make use of the API's
const ranges: vscode.Range[] = params.regions.map(element => new vscode.Range(element.startLine, 0, element.endLine, 0));
// Find entry for cached file and act accordingly
const valuePair: DecorationRangesPair | undefined = this.inactiveRegionsDecorations.get(params.uri);
if (valuePair) {
// Disposing of and resetting the decoration will undo previously applied text decorations
valuePair.decoration.dispose();
valuePair.decoration = decoration;
// As vscode.TextEditor.setDecorations only applies to visible editors, we must cache the range for when another editor becomes visible
valuePair.ranges = ranges;
} else { // The entry does not exist. Make a new one
const toInsert: DecorationRangesPair = {
decoration: decoration,
ranges: ranges
};
this.inactiveRegionsDecorations.set(params.uri, toInsert);
}
if (settings.dimInactiveRegions && params.fileVersion === openFileVersions.get(params.uri)) {
// Apply the decorations to all *visible* text editors
const editors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => e.document.uri.toString() === params.uri);
for (const e of editors) {
e.setDecorations(decoration, ranges);
}
}
}
if (this.codeFoldingProvider) {
private updateInactiveRegions(uriString: string, inactiveRegions: InputRegion[], startNewSet: boolean, updateFoldingRanges: boolean): void {
if (this.codeFoldingProvider && updateFoldingRanges) {
this.codeFoldingProvider.refresh();
}
const client: Client = clients.getClientFor(vscode.Uri.parse(uriString));
if (!(client instanceof DefaultClient) || (!startNewSet && inactiveRegions.length === 0)) {
return;
}
const settings: CppSettings = new CppSettings(client.RootUri);
const dimInactiveRegions: boolean = settings.dimInactiveRegions;
let currentSet: DecorationRangesPair | undefined = this.inactiveRegionsDecorations.get(uriString);
if (startNewSet || !dimInactiveRegions) {
if (currentSet) {
currentSet.decoration.dispose();
this.inactiveRegionsDecorations.delete(uriString);
}
if (!dimInactiveRegions) {
return;
}
currentSet = undefined;
}
if (currentSet === undefined) {
const opacity: number | undefined = settings.inactiveRegionOpacity;
currentSet = {
decoration: vscode.window.createTextEditorDecorationType({
opacity: (opacity === undefined) ? "0.55" : opacity.toString(),
backgroundColor: settings.inactiveRegionBackgroundColor,
color: settings.inactiveRegionForegroundColor,
rangeBehavior: vscode.DecorationRangeBehavior.OpenOpen
}),
ranges: []
};
this.inactiveRegionsDecorations.set(uriString, currentSet);
}
Array.prototype.push.apply(currentSet.ranges, inactiveRegions.map(element => new vscode.Range(element.startLine, 0, element.endLine, 0)));
// Apply the decorations to all *visible* text editors
const editors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => e.document.uri.toString() === uriString);
for (const e of editors) {
e.setDecorations(currentSet.decoration, currentSet.ranges);
}
}
public logIntelliSenseSetupTime(notification: IntelliSenseSetup): void {
@@ -2637,7 +2715,7 @@ export class DefaultClient implements Client {
return results;
}
private updateActiveDocumentTextOptions(): void {
public updateActiveDocumentTextOptions(): void {
const editor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
if (editor && util.isCpp(editor.document)) {
void SessionState.buildAndDebugIsSourceFile.set(util.isCppOrCFile(editor.document.uri));
@@ -2670,13 +2748,27 @@ export class DefaultClient implements Client {
/**
* notifications to the language server
*/
public async activeDocumentChanged(document: vscode.TextDocument): Promise<void> {
this.updateActiveDocumentTextOptions();
if (!util.isCpp(document)) {
public async didChangeActiveEditor(editor?: vscode.TextEditor): Promise<void> {
// For now, we ignore deactivation events.
// VS will refresh IntelliSense on activation, as a catch-all for file changes in
// other applications. But VS Code will deactivate the document when focus is moved
// to another control, such as the Output window. So, to avoid costly updates, we
// only trigger that update when focus moves from one C++ document to another.
// Fortunately, VS Code generates file-change notifications for all files
// in the workspace, so we should trigger appropriate updates for most changes
// made in other applications.
if (!editor || !util.isCpp(editor.document)) {
return;
}
await this.ready;
return this.languageClient.sendNotification(ActiveDocumentChangeNotification, this.languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document)).catch(logAndReturn.undefined);
this.updateActiveDocumentTextOptions();
const params: DidChangeActiveEditorParams = {
uri: editor?.document?.uri.toString(),
selection: editor ? makeLspRange(editor.selection) : undefined
};
return this.languageClient.sendNotification(DidChangeActiveEditorNotification, params).catch(logAndReturn.undefined);
}
/**
@@ -2696,8 +2788,7 @@ export class DefaultClient implements Client {
}
public async selectionChanged(selection: Range): Promise<void> {
await this.ready;
return this.languageClient.sendNotification(TextEditorSelectionChangeNotification, selection);
return this.languageClient.sendNotification(DidChangeTextEditorSelectionNotification, selection);
}
public async resetDatabase(): Promise<void> {
@@ -3531,7 +3622,6 @@ export class DefaultClient implements Client {
const isReplace: boolean = !range.isEmpty && isSourceFile;
lineOffset += nextLineOffset;
nextLineOffset = (edit.newText.match(/\n/g) ?? []).length;
let rangeStartLine: number = range.start.line + lineOffset;
// Find the editType.
if (isReplace) {
@@ -3553,6 +3643,8 @@ export class DefaultClient implements Client {
}
}
}
const formatRangeStartLine: number = range.start.line + lineOffset;
let rangeStartLine: number = formatRangeStartLine;
let rangeStartCharacter: number = 0;
if (edit.newText.startsWith("\r\n\r\n")) {
rangeStartCharacter = 4;
@@ -3567,15 +3659,15 @@ export class DefaultClient implements Client {
rangeStartCharacter = 1;
rangeStartLine += 1;
}
const newRange: vscode.Range = new vscode.Range(
new vscode.Position(rangeStartLine + (nextLineOffset < 0 ? nextLineOffset : 0), range.start.character),
const newFormatRange: vscode.Range = new vscode.Range(
new vscode.Position(formatRangeStartLine + (nextLineOffset < 0 ? nextLineOffset : 0), range.start.character),
new vscode.Position(rangeStartLine + (nextLineOffset < 0 ? 0 : nextLineOffset),
isReplace ? range.end.character :
range.end.character + edit.newText.length - rangeStartCharacter));
if (isSourceFile) {
sourceFormatUriAndRanges.push({uri, range: newRange});
sourceFormatUriAndRanges.push({uri, range: newFormatRange});
} else {
headerFormatUriAndRanges.push({uri, range: newRange});
headerFormatUriAndRanges.push({uri, range: newFormatRange});
}
if (isReplace || !isSourceFile) {
// Handle additional declaration lines added before the new function call.
@@ -3701,10 +3793,7 @@ export class DefaultClient implements Client {
// These events can be discarded until the language client is ready.
// Don't queue them up with this.notifyWhenLanguageClientReady calls.
if (this.innerLanguageClient !== undefined && this.configuration !== undefined) {
const params: IntervalTimerParams = {
freeMemory: Math.floor(os.freemem() / 1048576)
};
void this.languageClient.sendNotification(IntervalTimerNotification, params).catch(logAndReturn.undefined);
void this.languageClient.sendNotification(IntervalTimerNotification).catch(logAndReturn.undefined);
this.configuration.checkCppProperties();
this.configuration.checkCompileCommands();
}
@@ -3825,11 +3914,12 @@ class NullClient implements Client {
RootRealPath: string = "/";
RootUri?: vscode.Uri = vscode.Uri.file("/");
Name: string = "(empty)";
TrackedDocuments = new Set<vscode.TextDocument>();
TrackedDocuments = new Map<string, vscode.TextDocument>();
async onDidChangeSettings(event: vscode.ConfigurationChangeEvent): Promise<Record<string, string>> { return {}; }
onDidOpenTextDocument(document: vscode.TextDocument): void { }
onDidCloseTextDocument(document: vscode.TextDocument): void { }
onDidChangeVisibleTextEditor(editor: vscode.TextEditor): void { }
onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]): Promise<void> { return Promise.resolve(); }
onDidChangeTextEditorVisibleRanges(uri: vscode.Uri): Promise<void> { return Promise.resolve(); }
onDidChangeTextDocument(textDocumentChangeEvent: vscode.TextDocumentChangeEvent): void { }
onRegisterCustomConfigurationProvider(provider: CustomConfigurationProvider1): Thenable<void> { return Promise.resolve(); }
updateCustomConfigurations(requestingProvider?: CustomConfigurationProvider1): Thenable<void> { return Promise.resolve(); }
@@ -3845,10 +3935,11 @@ class NullClient implements Client {
getVcpkgEnabled(): Thenable<boolean> { return Promise.resolve(false); }
getCurrentCompilerPathAndArgs(): Thenable<util.CompilerPathAndArgs | undefined> { return Promise.resolve(undefined); }
getKnownCompilers(): Thenable<configs.KnownCompiler[] | undefined> { return Promise.resolve([]); }
takeOwnership(document: vscode.TextDocument): Promise<void> { return Promise.resolve(); }
takeOwnership(document: vscode.TextDocument): void { }
sendDidOpen(document: vscode.TextDocument): Promise<void> { return Promise.resolve(); }
requestSwitchHeaderSource(rootUri: vscode.Uri, fileName: string): Thenable<string> { return Promise.resolve(""); }
activeDocumentChanged(document: vscode.TextDocument): 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(); }
activate(): void { }
selectionChanged(selection: Range): void { }
@@ -5,7 +5,6 @@
'use strict';
import * as vscode from 'vscode';
import { logAndReturn } from '../Utility/Async/returns';
import * as util from '../common';
import * as telemetry from '../telemetry';
import * as cpptools from './client';
@@ -67,12 +66,13 @@ export class ClientCollection {
this.disposables.push(vscode.workspace.onDidChangeWorkspaceFolders(e => this.onDidChangeWorkspaceFolders(e)));
}
public async activeDocumentChanged(document: vscode.TextDocument): Promise<void> {
this.activeDocument = document;
const activeClient: cpptools.Client = this.getClientFor(document.uri);
public async didChangeActiveEditor(editor?: vscode.TextEditor): Promise<void> {
this.activeDocument = editor?.document;
// Notify the active client that the document has changed.
await activeClient.activeDocumentChanged(document);
// If there is no active document, switch to the default client.
const activeClient: cpptools.Client = !editor ? this.defaultClient : this.getClientFor(editor.document.uri);
await activeClient.didChangeActiveEditor(editor);
// If the active client changed, resume the new client and tell the currently active client to deactivate.
if (activeClient !== this.activeClient) {
@@ -121,7 +121,10 @@ export class ClientCollection {
const client: cpptools.Client = pair[1];
const newClient: cpptools.Client = this.createClient(client.RootFolder, true);
client.TrackedDocuments.forEach(document => void this.transferOwnership(document, client).catch(logAndReturn.undefined));
for (const document of client.TrackedDocuments.values()) {
this.transferOwnership(document, client);
await newClient.sendDidOpen(document);
}
if (this.activeClient === client) {
// It cannot be undefined. If there is an active document, we activate it later.
@@ -137,9 +140,12 @@ export class ClientCollection {
if (this.activeDocument) {
this.activeClient = this.getClientFor(this.activeDocument.uri);
await this.activeClient.activeDocumentChanged(this.activeDocument);
this.activeClient.updateActiveDocumentTextOptions();
this.activeClient.activate();
await this.activeClient.didChangeActiveEditor(vscode.window.activeTextEditor);
}
const cppEditors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => util.isCpp(e.document));
await this.defaultClient.onDidChangeVisibleTextEditors(cppEditors);
}
private async onDidChangeWorkspaceFolders(e?: vscode.WorkspaceFoldersChangeEvent): Promise<void> {
@@ -157,8 +163,7 @@ export class ClientCollection {
this.languageClients.delete(path); // Do this first so that we don't iterate on it during the ownership transfer process.
// Transfer ownership of the client's documents to another client.
// (this includes calling textDocument/didOpen on the new client so that the server knows it's open too)
client.TrackedDocuments.forEach(document => void this.transferOwnership(document, client).catch(logAndReturn.undefined));
client.TrackedDocuments.forEach(document => this.transferOwnership(document, client));
if (this.activeClient === client) {
this.activeClient.deactivate();
@@ -198,17 +203,17 @@ export class ClientCollection {
// Redundant deactivate should be OK.
this.activeClient.deactivate();
this.activeClient = newActiveClient;
await this.activeClient.activeDocumentChanged(this.activeDocument);
this.activeClient.updateActiveDocumentTextOptions();
this.activeClient.activate();
}
}
}
}
private async transferOwnership(document: vscode.TextDocument, oldOwner: cpptools.Client): Promise<void> {
private transferOwnership(document: vscode.TextDocument, oldOwner: cpptools.Client): void {
const newOwner: cpptools.Client = this.getClientFor(document.uri);
if (newOwner !== oldOwner) {
return newOwner.takeOwnership(document);
newOwner.takeOwnership(document);
}
}
@@ -170,11 +170,9 @@ export class CppBuildTaskProvider implements TaskProvider {
const compilerPathBase: string = path.basename(compilerPath);
const isCl: boolean = compilerPathBase.toLowerCase() === "cl.exe";
const isClang: boolean = !isCl && compilerPathBase.toLowerCase().includes("clang");
// Double-quote the command if it is not already double-quoted.
// Double-quote the command if needed.
let resolvedcompilerPath: string = isCl ? compilerPathBase : compilerPath;
if (resolvedcompilerPath && !resolvedcompilerPath.startsWith("\"") && resolvedcompilerPath.includes(" ")) {
resolvedcompilerPath = "\"" + resolvedcompilerPath + "\"";
}
resolvedcompilerPath = util.quoteArgument(resolvedcompilerPath);
if (!definition) {
const isWindows: boolean = os.platform() === 'win32';
@@ -209,7 +207,7 @@ export class CppBuildTaskProvider implements TaskProvider {
const task: CppBuildTask = new Task(definition, scope, definition.label, ext.CppSourceStr,
new CustomExecution(async (resolvedDefinition: TaskDefinition): Promise<Pseudoterminal> =>
// When the task is executed, this callback will run. Here, we setup for running the task.
new CustomBuildTaskTerminal(resolvedcompilerPath, resolvedDefinition.args, resolvedDefinition.options, {taskUsesActiveFile, insertStd: isClang && os.platform() === 'darwin'})
new CustomBuildTaskTerminal(resolvedcompilerPath, resolvedDefinition.args, resolvedDefinition.options, { taskUsesActiveFile, insertStd: isClang && os.platform() === 'darwin' })
), isCl ? '$msCompile' : '$gcc');
task.group = TaskGroup.Build;
@@ -390,7 +388,7 @@ class CustomBuildTaskTerminal implements Pseudoterminal {
util.createDirIfNotExistsSync(exePath);
this.args.forEach((value, index) => {
value = util.normalizeArg(util.resolveVariables(value));
value = util.quoteArgument(util.resolveVariables(value));
activeCommand = activeCommand + " " + value;
this.args[index] = value;
});
@@ -6,7 +6,6 @@
import * as vscode from 'vscode';
import { CustomConfigurationProvider, SourceFileConfigurationItem, Version, WorkspaceBrowseConfiguration } from 'vscode-cpptools';
import * as ext from './extension';
import { CppSettings } from './settings';
/**
@@ -158,7 +157,8 @@ export class CustomConfigurationProviderCollection {
}
public add(provider: CustomConfigurationProvider, version: Version): boolean {
if (new CppSettings(ext.getActiveClient().RootUri).intelliSenseEngine === "disabled") {
const settings: CppSettings = new CppSettings((vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) ? vscode.workspace.workspaceFolders[0]?.uri : undefined);
if (settings.intelliSenseEngine === "disabled") {
console.warn("Language service is disabled. Provider will not be registered.");
return false;
}
+37 -80
View File
@@ -28,7 +28,7 @@ import { PersistentState } from './persistentState';
import { NodeType, TreeNode } from './referencesModel';
import { CppSettings } from './settings';
import { LanguageStatusUI, getUI } from './ui';
import { makeCpptoolsRange, rangeEquals, shouldChangeFromCToCpp, showInstallCompilerWalkthrough } from './utils';
import { makeLspRange, rangeEquals, showInstallCompilerWalkthrough } from './utils';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
@@ -37,7 +37,7 @@ export const configPrefix: string = "C/C++: ";
let prevCrashFile: string;
export let clients: ClientCollection;
let activeDocument: string;
let activeDocument: vscode.TextDocument | undefined;
let ui: LanguageStatusUI;
const disposables: vscode.Disposable[] = [];
const commandDisposables: vscode.Disposable[] = [];
@@ -167,10 +167,11 @@ export async function activate(): Promise<void> {
});
disposables.push(vscode.workspace.onDidChangeConfiguration(onDidChangeSettings));
disposables.push(vscode.window.onDidChangeActiveTextEditor(onDidChangeActiveTextEditor));
ui.activeDocumentChanged(); // Handle already active documents (for non-cpp files that we don't register didOpen).
disposables.push(vscode.window.onDidChangeTextEditorSelection(onDidChangeTextEditorSelection));
disposables.push(vscode.window.onDidChangeVisibleTextEditors(onDidChangeVisibleTextEditors));
disposables.push(vscode.window.onDidChangeTextEditorVisibleRanges((e) => clients.ActiveClient.enqueue(async () => onDidChangeTextEditorVisibleRanges(e))));
disposables.push(vscode.window.onDidChangeActiveTextEditor((e) => clients.ActiveClient.enqueue(async () => onDidChangeActiveTextEditor(e))));
ui.didChangeActiveEditor(); // Handle already active documents (for non-cpp files that we don't register didOpen).
disposables.push(vscode.window.onDidChangeTextEditorSelection((e) => clients.ActiveClient.enqueue(async () => onDidChangeTextEditorSelection(e))));
disposables.push(vscode.window.onDidChangeVisibleTextEditors((e) => clients.ActiveClient.enqueue(async () => onDidChangeVisibleTextEditors(e))));
updateLanguageConfigurations();
@@ -244,6 +245,7 @@ export async function activate(): Promise<void> {
const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
if (activeEditor) {
clients.timeTelemetryCollector.setFirstFile(activeEditor.document.uri);
activeDocument = activeEditor.document;
}
}
@@ -278,7 +280,13 @@ async function onDidChangeSettings(event: vscode.ConfigurationChangeEvent): Prom
let noActiveEditorTimeout: NodeJS.Timeout | undefined;
export function onDidChangeActiveTextEditor(editor?: vscode.TextEditor): void {
async function onDidChangeTextEditorVisibleRanges(event: vscode.TextEditorVisibleRangesChangeEvent): Promise<void> {
if (util.isCpp(event.textEditor.document)) {
await clients.getDefaultClient().onDidChangeTextEditorVisibleRanges(event.textEditor.document.uri);
}
}
function onDidChangeActiveTextEditor(editor?: vscode.TextEditor): void {
/* need to notify the affected client(s) */
console.assert(clients !== undefined, "client should be available before active editor is changed");
if (clients === undefined) {
@@ -289,93 +297,42 @@ export function onDidChangeActiveTextEditor(editor?: vscode.TextEditor): void {
clearTimeout(noActiveEditorTimeout);
noActiveEditorTimeout = undefined;
}
if (!editor) {
// When switching between documents, VS Code is setting the active editor to undefined
// temporarily, so this prevents the C++-related status bar items from flickering off/on.
noActiveEditorTimeout = setTimeout(() => {
activeDocument = "";
ui.activeDocumentChanged();
activeDocument = undefined;
ui.didChangeActiveEditor();
noActiveEditorTimeout = undefined;
}, 100);
return;
}
if (util.isCppOrRelated(editor.document)) {
// This is required for the UI to update correctly.
void clients.activeDocumentChanged(editor.document).catch(logAndReturn.undefined);
if (util.isCpp(editor.document)) {
activeDocument = editor.document.uri.toString();
clients.ActiveClient.selectionChanged(makeCpptoolsRange(editor.selection));
} else {
activeDocument = "";
}
void clients.didChangeActiveEditor(undefined).catch(logAndReturn.undefined);
} else {
activeDocument = "";
ui.didChangeActiveEditor();
if (util.isCppOrRelated(editor.document)) {
if (util.isCpp(editor.document)) {
activeDocument = editor.document;
void clients.didChangeActiveEditor(editor).catch(logAndReturn.undefined);
} else {
activeDocument = undefined;
void clients.didChangeActiveEditor(undefined).catch(logAndReturn.undefined);
}
//clients.ActiveClient.selectionChanged(makeLspRange(editor.selection));
} else {
activeDocument = undefined;
}
}
getUI().activeDocumentChanged();
}
function onDidChangeTextEditorSelection(event: vscode.TextEditorSelectionChangeEvent): void {
/* need to notify the affected client(s) */
if (!event.textEditor || !vscode.window.activeTextEditor || event.textEditor.document.uri !== vscode.window.activeTextEditor.document.uri ||
!util.isCpp(event.textEditor.document)) {
if (!util.isCpp(event.textEditor.document)) {
return;
}
if (activeDocument !== event.textEditor.document.uri.toString()) {
// For some unknown reason we don't reliably get onDidChangeActiveTextEditor callbacks.
activeDocument = event.textEditor.document.uri.toString();
void clients.activeDocumentChanged(event.textEditor.document).catch(logAndReturn.undefined);
ui.activeDocumentChanged();
}
clients.ActiveClient.selectionChanged(makeCpptoolsRange(event.selections[0]));
clients.ActiveClient.selectionChanged(makeLspRange(event.selections[0]));
}
export async function processDelayedDidOpen(document: vscode.TextDocument): Promise<boolean> {
const client: Client = clients.getClientFor(document.uri);
if (client) {
// Log warm start.
if (clients.checkOwnership(client, document)) {
if (!client.isInitialized()) {
// This can randomly get hit when adding/removing workspace folders.
await client.ready;
}
// Do not call await between TrackedDocuments.has() and TrackedDocuments.add(),
// to avoid sending redundant didOpen notifications.
if (!client.TrackedDocuments.has(document)) {
// If not yet tracked, process as a newly opened file. (didOpen is sent to server in client.takeOwnership()).
client.TrackedDocuments.add(document);
clients.timeTelemetryCollector.setDidOpenTime(document.uri);
// Work around vscode treating ".C" or ".H" as c, by adding this file name to file associations as cpp
if (document.languageId === "c" && shouldChangeFromCToCpp(document)) {
const baseFileName: string = path.basename(document.fileName);
const mappingString: string = baseFileName + "@" + document.fileName;
client.addFileAssociations(mappingString, "cpp");
client.sendDidChangeSettings();
document = await vscode.languages.setTextDocumentLanguage(document, "cpp");
}
await client.provideCustomConfiguration(document.uri, undefined);
// client.takeOwnership() will call client.TrackedDocuments.add() again, but that's ok. It's a Set.
client.onDidOpenTextDocument(document);
await client.takeOwnership(document);
return true;
}
}
}
return false;
}
function onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]): void {
// Process delayed didOpen for any visible editors we haven't seen before
// eslint-disable-next-line @typescript-eslint/no-misused-promises
editors.forEach(async (editor) => {
if (util.isCpp(editor.document)) {
const client: Client = clients.getClientFor(editor.document.uri);
await client.enqueue(() => processDelayedDidOpen(editor.document));
client.onDidChangeVisibleTextEditor(editor);
}
});
async function onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]): Promise<void> {
const cppEditors: vscode.TextEditor[] = editors.filter(e => util.isCpp(e.document));
await clients.getDefaultClient().onDidChangeVisibleTextEditors(cppEditors);
}
function onInterval(): void {
@@ -671,7 +628,7 @@ async function onGoToPrevDirectiveInGroup(): Promise<void> {
}
async function onRunCodeAnalysisOnActiveFile(): Promise<void> {
if (activeDocument !== "") {
if (activeDocument) {
await vscode.commands.executeCommand("workbench.action.files.saveAll");
return getActiveClient().handleRunCodeAnalysisOnActiveFile();
}
+43 -25
View File
@@ -4,11 +4,15 @@
* ------------------------------------------------------------------------------------------ */
'use strict';
import * as path from 'path';
import * as vscode from 'vscode';
import { Middleware } from 'vscode-languageclient';
import * as util from '../common';
import { Client } from './client';
import { clients, onDidChangeActiveTextEditor, processDelayedDidOpen } from './extension';
import { clients } from './extension';
import { shouldChangeFromCToCpp } from './utils';
let anyFileOpened: boolean = false;
export function createProtocolFilter(): Middleware {
// Disabling lint for invoke handlers
@@ -18,29 +22,42 @@ export function createProtocolFilter(): Middleware {
const invoke4 = (a: any, b: any, c: any, d: any, next: (a: any, b: any, c: any, d: any) => any): any => clients.ActiveClient.enqueue(() => next(a, b, c, d));
return {
didOpen: async (document, _sendMessage) => {
if (util.isCpp(document)) {
util.setWorkspaceIsCpp();
didOpen: async (document, sendMessage) => clients.ActiveClient.enqueue(async () => {
if (!util.isCpp(document)) {
return;
}
const editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.find(e => e.document === document);
if (editor) {
// If the file was visible editor when we were activated, we will not get a call to
// onDidChangeVisibleTextEditors, so immediately open any file that is visible when we receive didOpen.
// Otherwise, we defer opening the file until it's actually visible.
await clients.ActiveClient.enqueue(() => processDelayedDidOpen(document));
if (editor && editor === vscode.window.activeTextEditor) {
onDidChangeActiveTextEditor(editor);
util.setWorkspaceIsCpp();
const client: Client = clients.getClientFor(document.uri);
if (clients.checkOwnership(client, document)) {
const uriString: string = document.uri.toString();
if (!client.TrackedDocuments.has(uriString)) {
client.TrackedDocuments.set(uriString, document);
// Work around vscode treating ".C" or ".H" as c, by adding this file name to file associations as cpp
if (document.languageId === "c" && shouldChangeFromCToCpp(document)) {
const baseFileName: string = path.basename(document.fileName);
const mappingString: string = baseFileName + "@" + document.fileName;
client.addFileAssociations(mappingString, "cpp");
client.sendDidChangeSettings();
document = await vscode.languages.setTextDocumentLanguage(document, "cpp");
}
await client.provideCustomConfiguration(document.uri, undefined);
// client.takeOwnership() will call client.TrackedDocuments.add() again, but that's ok. It's a Set.
client.onDidOpenTextDocument(document);
client.takeOwnership(document);
await sendMessage(document);
// For a file already open when we activate, sometimes we don't get any notifications about visible
// or active text editors, visible ranges, or text selection. As a workaround, we trigger
// onDidChangeVisibleTextEditors here, only for the first file opened.
if (!anyFileOpened)
{
anyFileOpened = true;
const cppEditors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => util.isCpp(e.document));
await client.onDidChangeVisibleTextEditors(cppEditors);
}
}
} else {
// NO-OP
// If the file is not opened into an editor (such as in response for a control-hover),
// we do not actually load a translation unit for it. When we receive a didOpen, the file
// may not yet be visible. So, we defer creation of the translation until we receive a
// call to onDidChangeVisibleTextEditors(), in extension.ts. A file is only loaded when
// it is actually opened in the editor (not in response to control-hover, which sends a
// didOpen), and first becomes visible.
}
},
}),
didChange: async (textDocumentChangeEvent, sendMessage) => clients.ActiveClient.enqueue(async () => {
const me: Client = clients.getClientFor(textDocumentChangeEvent.document.uri);
me.onDidChangeTextDocument(textDocumentChangeEvent);
@@ -52,7 +69,7 @@ export function createProtocolFilter(): Middleware {
// Don't use awaitUntilLanguageClientReady.
// Otherwise, the message can be delayed too long.
const me: Client = clients.getClientFor(event.document.uri);
if (me.TrackedDocuments.has(event.document)) {
if (me.TrackedDocuments.has(event.document.uri.toString())) {
return sendMessage(event);
}
return [];
@@ -60,9 +77,10 @@ export function createProtocolFilter(): Middleware {
didSave: invoke1,
didClose: async (document, sendMessage) => clients.ActiveClient.enqueue(async () => {
const me: Client = clients.getClientFor(document.uri);
if (me.TrackedDocuments.has(document)) {
const uriString: string = document.uri.toString();
if (me.TrackedDocuments.has(uriString)) {
me.onDidCloseTextDocument(document);
me.TrackedDocuments.delete(document);
me.TrackedDocuments.delete(uriString);
await sendMessage(document);
}
}),
@@ -70,7 +88,7 @@ export function createProtocolFilter(): Middleware {
resolveCompletionItem: invoke2,
provideHover: async (document, position, token, next: (document: any, position: any, token: any) => any) => clients.ActiveClient.enqueue(async () => {
const me: Client = clients.getClientFor(document.uri);
if (me.TrackedDocuments.has(document)) {
if (me.TrackedDocuments.has(document.uri.toString())) {
return next(document, position, token);
}
return null;
@@ -368,6 +368,10 @@ export class ReferencesManager {
this.referencesProgressBarStartTime = Date.now();
this.clearViews();
if (this.referencesDelayProgress) {
clearInterval(this.referencesDelayProgress);
}
this.referencesDelayProgress = setInterval(() => {
const progressTitle: string = referencesCommandModeToString(this.client.ReferencesCommandMode);
this.referencesProgressOptions = { location: vscode.ProgressLocation.Notification, title: progressTitle, cancellable: true };
+1 -1
View File
@@ -495,7 +495,7 @@ export class LanguageStatusUI {
}
}
public activeDocumentChanged(): void {
public didChangeActiveEditor(): void {
const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
if (!activeEditor) {
this.ShowConfiguration = false;
+1 -1
View File
@@ -10,7 +10,7 @@ import { SessionState } from '../sessionState';
import { Location, TextEdit } from './commonTypes';
import { CppSettings } from './settings';
export function makeCpptoolsRange(vscRange: vscode.Range): Range {
export function makeLspRange(vscRange: vscode.Range): Range {
return {
start: { line: vscRange.start.line, character: vscRange.start.character },
end: { line: vscRange.end.line, character: vscRange.end.character }
+87 -19
View File
@@ -11,7 +11,7 @@ import * as os from 'os';
import * as path from 'path';
import * as tmp from 'tmp';
import * as vscode from 'vscode';
import { DocumentFilter } from 'vscode-languageclient';
import { DocumentFilter, Range } from 'vscode-languageclient';
import * as nls from 'vscode-nls';
import { TargetPopulation } from 'vscode-tas-client';
import * as which from "which";
@@ -1318,7 +1318,7 @@ export function getCacheStoragePath(): string {
defaultCachePath = "vscode-cpptools/";
pathEnvironmentVariable = process.env.XDG_CACHE_HOME;
if (!pathEnvironmentVariable) {
pathEnvironmentVariable = os.homedir();
pathEnvironmentVariable = path.join(os.homedir(), ".cache");
}
break;
}
@@ -1360,25 +1360,55 @@ export function sequentialResolve<T>(items: T[], promiseBuilder: (item: T) => Pr
}, Promise.resolve());
}
export function normalizeArg(arg: string): string {
arg = arg.trim();
// Check if the arg is enclosed in backtick,
// or includes unescaped double-quotes (or single-quotes on Windows),
// or includes unescaped single-quotes on mac and linux.
if (/^`.*`$/g.test(arg) || /.*[^\\]".*/g.test(arg) ||
(process.platform.includes("win") && /.*[^\\]'.*/g.test(arg)) ||
(!process.platform.includes("win") && /.*[^\\]'.*/g.test(arg))) {
return arg;
export function quoteArgument(argument: string): string {
// Return the argument as is if it's empty
if (!argument) {
return argument;
}
// The special character double-quote is already escaped in the arg.
const unescapedSpaces: string | undefined = arg.split('').find((char, index) => index > 0 && char === " " && arg[index - 1] !== "\\");
if (!unescapedSpaces && !process.platform.includes("win")) {
return arg;
} else if (arg.includes(" ")) {
arg = arg.replace(/\\\s/g, " ");
return "\"" + arg + "\"";
if (os.platform() === "win32") {
// Windows-style quoting logic
if (!/[\s\t\n\v\"\\&%^]/.test(argument)) {
return argument;
}
let quotedArgument = '"';
let backslashCount = 0;
for (const char of argument) {
if (char === '\\') {
backslashCount++;
} else {
if (char === '"') {
quotedArgument += '\\'.repeat(backslashCount * 2 + 1);
} else {
quotedArgument += '\\'.repeat(backslashCount);
}
quotedArgument += char;
backslashCount = 0;
}
}
quotedArgument += '\\'.repeat(backslashCount * 2);
quotedArgument += '"';
return quotedArgument;
} else {
return arg;
// Unix-style quoting logic
if (!/[\s\t\n\v\"'\\$`|;&(){}<>*?!\[\]~^#%]/.test(argument)) {
return argument;
}
let quotedArgument = "'";
for (const c of argument) {
if (c === "'") {
quotedArgument += "'\\''";
} else {
quotedArgument += c;
}
}
quotedArgument += "'";
return quotedArgument;
}
}
@@ -1546,3 +1576,41 @@ export function getNumericLoggingLevel(loggingLevel: string | undefined): number
return 0;
}
}
export function mergeOverlappingRanges(ranges: Range[]): Range[] {
// Fix any reversed ranges. Not sure if this is needed, but ensures the input is sanitized.
const mergedRanges: Range[] = ranges.map(range => {
if (range.start.line > range.end.line || (range.start.line === range.end.line && range.start.character > range.end.character)) {
return Range.create(range.end, range.start);
}
return range;
});
// Merge overlapping ranges.
mergedRanges.sort((a, b) => a.start.line - b.start.line || a.start.character - b.start.character);
let lastMergedIndex = 0; // Index to keep track of the last merged range
for (let currentIndex = 0; currentIndex < ranges.length; currentIndex++) {
const currentRange = ranges[currentIndex]; // No need for a shallow copy, since we're not modifying the ranges we haven't read yet.
let nextIndex = currentIndex + 1;
while (nextIndex < ranges.length) {
const nextRange = ranges[nextIndex];
// Check for non-overlapping ranges first
if (nextRange.start.line > currentRange.end.line ||
(nextRange.start.line === currentRange.end.line && nextRange.start.character > currentRange.end.character)) {
break;
}
// Otherwise, merge the overlapping ranges
currentRange.end = {
line: Math.max(currentRange.end.line, nextRange.end.line),
character: Math.max(currentRange.end.character, nextRange.end.character)
};
nextIndex++;
}
// Overwrite the array in-place
mergedRanges[lastMergedIndex] = currentRange;
lastMergedIndex++;
currentIndex = nextIndex - 1; // Skip the merged ranges
}
mergedRanges.length = lastMergedIndex;
return mergedRanges;
}
@@ -6,7 +6,7 @@ import * as assert from "assert";
import { suite } from 'mocha';
import * as os from "os";
import { delimiter } from 'path';
import { escapeForSquiggles, normalizeArg, resolveVariables } from "../../../../src/common";
import { escapeForSquiggles, quoteArgument, resolveVariables } from "../../../../src/common";
suite("resolveVariables", () => {
const success: string = "success";
@@ -219,39 +219,36 @@ suite("resolveVariables", () => {
testEscapeForSquigglesScenario("\"\\\\\"", "\"\\\\\\\\\""); // quoted string containing escaped backslash
});
test("normalizeArgs:", () => {
const testNormalizeArgsScenario: any = (input: string, expectedOutput: string) => {
const result: string = normalizeArg(input);
test("quoteArgument:", () => {
const testQuoteArgumentScenario: any = (input: string, expectedOutput: string) => {
const result: string = quoteArgument(input);
if (result !== expectedOutput) {
throw new Error(`normalizeArgs failure: for \"${input}\", \"${result}\" !== \"${expectedOutput}\"`);
throw new Error(`quoteArgument failure: for \"${input}\", \"${result}\" !== \"${expectedOutput}\"`);
}
};
/*
this is how the args from tasks.json will be sent to the chilprocess.spawn:
"args":[
"-DTEST1=TEST1 TEST1", // "-DTEST1=TEST1 TEST1"
"-DTEST2=\"TEST2 TEST2\"", // -DTEST2="TEST2 TEST2"
"-DTEST3=\\\"TEST3 TEST3\\\"", // "-DTEST3=\"TEST3 TEST3\""
"-DTEST4=TEST4\\ TEST4", // "-DTEST4=TEST4 TEST4"
"-DTEST5='TEST5 TEST5'", // -DTEST5='TEST5 TEST5'
"-DTEST6=TEST6\\ TEST6 Test6", // "-DTEST6=TEST6 TEST6 Test6"
]
*/
testNormalizeArgsScenario("-DTEST1=TEST1 TEST1", "\"-DTEST1=TEST1 TEST1\"");
testNormalizeArgsScenario("-DTEST2=\"TEST2 TEST2\"", "-DTEST2=\"TEST2 TEST2\"");
testNormalizeArgsScenario("-DTEST3=\\\"TEST3 TEST3\\\"", "\"-DTEST3=\\\"TEST3 TEST3\\\"\"");
if (process.platform.includes("win")) {
testNormalizeArgsScenario("-DTEST4=TEST4\\ TEST4", "\"-DTEST4=TEST4 TEST4\"");
testNormalizeArgsScenario("-DTEST5=\'TEST5 TEST5\'", "-DTEST5=\'TEST5 TEST5\'");
} else {
testNormalizeArgsScenario("-DTEST4=TEST4\\ TEST4", "-DTEST4=TEST4\\ TEST4");
testNormalizeArgsScenario("-DTEST5='TEST5 TEST5'", "-DTEST5='TEST5 TEST5'");
}
testNormalizeArgsScenario("-DTEST6=TEST6\\ TEST6 Test6", "\"-DTEST6=TEST6 TEST6 Test6\"");
/*
this is how the args from tasks.json will be sent to the chilprocess.spawn:
"args":[
"-DTEST1=TEST1 TEST1", // "-DTEST1=TEST1 TEST1"
"-DTEST2=\"TEST2 TEST2\"", // -DTEST2="TEST2 TEST2"
"-DTEST3=\\\"TEST3 TEST3\\\"", // "-DTEST3=\"TEST3 TEST3\""
"-DTEST4=TEST4\\ TEST4", // "-DTEST4=TEST4 TEST4"
"-DTEST5='TEST5 TEST5'", // -DTEST5='TEST5 TEST5'
"-DTEST6=TEST6\\ TEST6 Test6", // "-DTEST6=TEST6 TEST6 Test6"
]
*/
testQuoteArgumentScenario("-DTEST1=TEST1 TEST1", "\"-DTEST1=TEST1 TEST1\"");
testQuoteArgumentScenario("-DTEST2=\"TEST2 TEST2\"", "-DTEST2=\"TEST2 TEST2\"");
testQuoteArgumentScenario("-DTEST3=\\\"TEST3 TEST3\\\"", "\"-DTEST3=\\\"TEST3 TEST3\\\"\"");
testQuoteArgumentScenario("-DTEST4=TEST4\\ TEST4", "\"-DTEST4=TEST4 TEST4\"");
testQuoteArgumentScenario("-DTEST5=\'TEST5 TEST5\'", "-DTEST5=\'TEST5 TEST5\'");
testQuoteArgumentScenario("-DTEST4=TEST4\\ TEST4", "-DTEST4=TEST4\\ TEST4");
testQuoteArgumentScenario("-DTEST5='TEST5 TEST5'", "-DTEST5='TEST5 TEST5'");
testQuoteArgumentScenario("-DTEST6=TEST6\\ TEST6 Test6", "\"-DTEST6=TEST6 TEST6 Test6\"");
});
interface ResolveTestFlowEnvironment {
withEnvironment(additionalEnvironment: {[key: string]: string | string[]}): ResolveTestFlowAssert;
withEnvironment(additionalEnvironment: { [key: string]: string | string[] }): ResolveTestFlowAssert;
shouldLookupSymbol(key: string): void;
}
interface ResolveTestFlowAssert {
@@ -260,9 +257,9 @@ suite("resolveVariables", () => {
function resolveVariablesWithInput(input: string): ResolveTestFlowEnvironment {
return {
withEnvironment: (additionalEnvironment: {[key: string]: string | string[]}) => inputAndEnvironment(input, additionalEnvironment),
withEnvironment: (additionalEnvironment: { [key: string]: string | string[] }) => inputAndEnvironment(input, additionalEnvironment),
shouldLookupSymbol: (symbol: string) => {
const environment: {[key: string]: string | string[]} = {};
const environment: { [key: string]: string | string[] } = {};
environment[symbol] = success;
return inputAndEnvironment(input, environment)
.shouldResolveTo(success);
@@ -270,7 +267,7 @@ suite("resolveVariables", () => {
};
}
function inputAndEnvironment(input: string, additionalEnvironment: {[key: string]: string | string[]}): ResolveTestFlowAssert {
function inputAndEnvironment(input: string, additionalEnvironment: { [key: string]: string | string[] }): ResolveTestFlowAssert {
return {
shouldResolveTo: (expected: string) => {
const actual: string = resolveVariables(input, additionalEnvironment);