Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
212ea02e4c | ||
|
|
ab8b90b187 | ||
|
|
8d42c49b77 | ||
|
|
0e24296f67 | ||
|
|
7f87dc49f8 | ||
|
|
cc4177b096 | ||
|
|
a25b309ac7 | ||
|
|
b6f48cd1fe | ||
|
|
ea391a3a17 |
+20
-1
@@ -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
|
||||
|
||||
+3558
-3558
File diff suppressed because it is too large
Load Diff
+3558
-3558
File diff suppressed because it is too large
Load Diff
+3558
-3558
File diff suppressed because it is too large
Load Diff
+3558
-3558
File diff suppressed because it is too large
Load Diff
+3558
-3558
File diff suppressed because it is too large
Load Diff
+3558
-3558
File diff suppressed because it is too large
Load Diff
+3558
-3558
File diff suppressed because it is too large
Load Diff
+3558
-3558
File diff suppressed because it is too large
Load Diff
+3558
-3558
File diff suppressed because it is too large
Load Diff
+3558
-3558
File diff suppressed because it is too large
Load Diff
+3558
-3558
File diff suppressed because it is too large
Load Diff
+3558
-3558
File diff suppressed because it is too large
Load Diff
+3558
-3558
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user