Skip to content

Commit 1aced54

Browse files
authored
Merge pull request #12090 from microsoft/coleng/cherry_pick_3_11_2024
2 parents e037b95 + a8cf39c commit 1aced54

File tree

4 files changed

+68
-45
lines changed

4 files changed

+68
-45
lines changed

Extension/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
### Bug Fixes
55
* Fix some potential deadlocks. [#12051](https://github.com/microsoft/vscode-cpptools/issues/12051)
66
* Fix a crash related to parsing concepts. [#12060](https://github.com/microsoft/vscode-cpptools/issues/12060)
7+
* Fix flickering status updates in the language status bar. [#12084](https://github.com/microsoft/vscode-cpptools/issues/12084)
78
* Fix a cpptools crash that can occur if cpptools-srv crashes on initialization.
89

910
## Version 1.19.6: March 6, 2024

Extension/src/LanguageServer/client.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -629,10 +629,13 @@ class ClientModel {
629629
constructor() {
630630
this.isInitializingWorkspace = new DataBinding<boolean>(false);
631631
this.isIndexingWorkspace = new DataBinding<boolean>(false);
632-
this.isParsingWorkspace = new DataBinding<boolean>(false);
633-
this.isParsingWorkspacePaused = new DataBinding<boolean>(false);
634-
this.isParsingFiles = new DataBinding<boolean>(false);
635-
this.isUpdatingIntelliSense = new DataBinding<boolean>(false);
632+
633+
// The following elements add a delay of 500ms before notitfying the UI that the icon can hide itself.
634+
this.isParsingWorkspace = new DataBinding<boolean>(false, 500, false);
635+
this.isParsingWorkspacePaused = new DataBinding<boolean>(false, 500, false);
636+
this.isParsingFiles = new DataBinding<boolean>(false, 500, false);
637+
this.isUpdatingIntelliSense = new DataBinding<boolean>(false, 500, false);
638+
636639
this.isRunningCodeAnalysis = new DataBinding<boolean>(false);
637640
this.isCodeAnalysisPaused = new DataBinding<boolean>(false);
638641
this.codeAnalysisProcessed = new DataBinding<number>(0);
@@ -3689,9 +3692,9 @@ export class DefaultClient implements Client {
36893692
isReplace ? range.end.character :
36903693
range.end.character + edit.newText.length - rangeStartCharacter));
36913694
if (isSourceFile) {
3692-
sourceFormatUriAndRanges.push({uri, range: newFormatRange});
3695+
sourceFormatUriAndRanges.push({ uri, range: newFormatRange });
36933696
} else {
3694-
headerFormatUriAndRanges.push({uri, range: newFormatRange});
3697+
headerFormatUriAndRanges.push({ uri, range: newFormatRange });
36953698
}
36963699
if (isReplace || !isSourceFile) {
36973700
// Handle additional declaration lines added before the new function call.
@@ -3741,7 +3744,8 @@ export class DefaultClient implements Client {
37413744
// without being opened because otherwise users may not realize that
37423745
// the header had changed (unless they view source control differences).
37433746
await vscode.window.showTextDocument(headerFormatUriAndRanges[0].uri, {
3744-
selection: headerReplaceEditRange, preserveFocus: false });
3747+
selection: headerReplaceEditRange, preserveFocus: false
3748+
});
37453749
}
37463750

37473751
// Format the new text edits.
@@ -3785,8 +3789,7 @@ export class DefaultClient implements Client {
37853789
formatEdits.set(formatUriAndRange.uri, formatTextEdits);
37863790
return true;
37873791
};
3788-
if (!await tryFormat())
3789-
{
3792+
if (!await tryFormat()) {
37903793
await tryFormat(); // Try again;
37913794
}
37923795
};
@@ -3797,15 +3800,17 @@ export class DefaultClient implements Client {
37973800
// This showTextDocument is required in order to get the selection to be
37983801
// correct after the formatting edit is applied. It could be a VS Code bug.
37993802
await vscode.window.showTextDocument(headerFormatUriAndRanges[0].uri, {
3800-
selection: headerReplaceEditRange, preserveFocus: false });
3803+
selection: headerReplaceEditRange, preserveFocus: false
3804+
});
38013805
await vscode.workspace.applyEdit(formatEdits, { isRefactoring: true });
38023806
formatEdits = new vscode.WorkspaceEdit();
38033807
}
38043808
}
38053809

38063810
// Select the replaced code.
38073811
await vscode.window.showTextDocument(sourceFormatUriAndRanges[0].uri, {
3808-
selection: sourceReplaceEditRange, preserveFocus: false });
3812+
selection: sourceReplaceEditRange, preserveFocus: false
3813+
});
38093814

38103815
await formatRanges(sourceFormatUriAndRanges);
38113816
if (formatEdits.size > 0) {

Extension/src/LanguageServer/dataBinding.ts

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,37 @@
44
* ------------------------------------------------------------------------------------------ */
55
import * as vscode from 'vscode';
66

7+
class Deferral {
8+
private timer?: NodeJS.Timeout;
9+
10+
constructor(callback: () => void, timeout: number) {
11+
this.timer = setTimeout(() => {
12+
this.timer = undefined;
13+
callback();
14+
}, timeout);
15+
}
16+
public cancel() {
17+
if (this.timer) {
18+
clearTimeout(this.timer);
19+
this.timer = undefined;
20+
}
21+
}
22+
}
23+
724
export class DataBinding<T> {
8-
private value: T;
925
private valueChanged = new vscode.EventEmitter<T>();
1026
private isActive: boolean = true;
27+
private deferral?: Deferral;
1128

12-
constructor(value: T) {
13-
this.value = value;
29+
/**
30+
* Bind an event to a value so that a data model can automatically update the UI when values change.
31+
* Since values can change quickly and cause UI to flicker, an optional delay/trigger combination can
32+
* be specified to prevent UI elements from appearing/disappearing too quickly.
33+
* @param value The initial value in the binding.
34+
* @param delay An optional delay (in milliseconds) for firing the value changed event.
35+
* @param delayValueTrigger The value that triggers an event delay.
36+
*/
37+
constructor(private value: T, private delay: number = 0, private delayValueTrigger?: T) {
1438
this.isActive = true;
1539
}
1640

@@ -20,8 +44,21 @@ export class DataBinding<T> {
2044

2145
public set Value(value: T) {
2246
if (value !== this.value) {
23-
this.value = value;
24-
this.valueChanged.fire(this.value);
47+
if (this.delay === 0 || value !== this.delayValueTrigger) {
48+
this.value = value;
49+
this.valueChanged.fire(this.value);
50+
} else {
51+
if (this.deferral) {
52+
this.deferral.cancel();
53+
}
54+
this.deferral = new Deferral(() => {
55+
this.value = value;
56+
this.valueChanged.fire(this.value);
57+
}, this.delay);
58+
}
59+
} else if (this.deferral) {
60+
this.deferral.cancel();
61+
this.deferral = undefined;
2562
}
2663
}
2764

Extension/src/LanguageServer/ui.ts

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@ const commandArguments: string[] = []; // We report the sender of the command
4646
export class LanguageStatusUI {
4747
private currentClient: Client | undefined;
4848

49-
// Timer for icons from appearing too often and for too short of a time.
50-
private readonly iconDelayTime: number = 1000;
51-
5249
// IntelliSense language status
5350
private intelliSenseStatusItem: vscode.LanguageStatusItem;
5451
private readonly updatingIntelliSenseText: string = localize("updating.intellisense.text", "IntelliSense: Updating");
@@ -58,7 +55,6 @@ export class LanguageStatusUI {
5855
private isParsingWorkspace: boolean = false;
5956
private isParsingWorkspacePaused: boolean = false;
6057
private isParsingFiles: boolean = false;
61-
private tagParseTimeout?: NodeJS.Timeout;
6258
private readonly dataBaseIcon: string = "$(database)";
6359
private readonly workspaceParsingInitializing: string = localize("initializing.tagparser.text", "Initializing Workspace");
6460
private readonly workspaceParsingIndexing: string = localize("indexing.tagparser.text", "Indexing Workspace");
@@ -115,25 +111,15 @@ export class LanguageStatusUI {
115111
return item;
116112
}
117113

118-
private flameTimeout?: NodeJS.Timeout;
119114
private setIsUpdatingIntelliSense(val: boolean): void {
120115
this.intelliSenseStatusItem.busy = val;
121116

122-
if (this.flameTimeout) {
123-
clearTimeout(this.flameTimeout);
124-
}
125-
126117
if (val) {
127118
this.intelliSenseStatusItem.text = "$(flame)";
128119
this.intelliSenseStatusItem.detail = this.updatingIntelliSenseText;
129-
this.flameTimeout = undefined;
130120
} else {
131-
this.flameTimeout = setTimeout(() => {
132-
if (this.intelliSenseStatusItem) {
133-
this.intelliSenseStatusItem.text = this.idleIntelliSenseText;
134-
this.intelliSenseStatusItem.detail = "";
135-
}
136-
}, this.iconDelayTime);
121+
this.intelliSenseStatusItem.text = this.idleIntelliSenseText;
122+
this.intelliSenseStatusItem.detail = "";
137123
}
138124
this.intelliSenseStatusItem.command = {
139125
command: "C_Cpp.RestartIntelliSenseForFile",
@@ -220,10 +206,6 @@ export class LanguageStatusUI {
220206
private setTagParseStatus(): void {
221207
// Set busy icon outside of timer for more real-time response
222208
this.tagParseStatusItem.busy = (this.isParsingWorkspace && !this.isParsingWorkspacePaused) || this.isParsingFiles;
223-
if (this.tagParseStatusItem.busy && this.tagParseTimeout) {
224-
clearTimeout(this.tagParseTimeout);
225-
this.tagParseTimeout = undefined;
226-
}
227209

228210
if (this.isParsingWorkspace || this.isParsingFiles) {
229211
this.tagParseStatusItem.text = this.dataBaseIcon;
@@ -251,15 +233,13 @@ export class LanguageStatusUI {
251233
}
252234
} else {
253235
// Parsing completed.
254-
this.tagParseTimeout = setTimeout(() => {
255-
this.tagParseStatusItem.text = this.workspaceParsingDoneText;
256-
this.tagParseStatusItem.detail = "";
257-
this.tagParseStatusItem.command = {
258-
command: "C_Cpp.RescanWorkspace",
259-
title: this.workspaceRescanText,
260-
arguments: commandArguments
261-
};
262-
}, this.iconDelayTime);
236+
this.tagParseStatusItem.text = this.workspaceParsingDoneText;
237+
this.tagParseStatusItem.detail = "";
238+
this.tagParseStatusItem.command = {
239+
command: "C_Cpp.RescanWorkspace",
240+
title: this.workspaceRescanText,
241+
arguments: commandArguments
242+
};
263243
}
264244
}
265245
//#endregion Tag parse language status

0 commit comments

Comments
 (0)