Skip to content

Commit a179a4f

Browse files
committed
Strict null check suggest widget
1 parent 824d1ce commit a179a4f

5 files changed

Lines changed: 47 additions & 31 deletions

File tree

src/tsconfig.strictNullChecks.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,9 @@
241241
"./vs/editor/contrib/snippet/test/snippetVariables.test.ts",
242242
"./vs/editor/contrib/suggest/completionModel.ts",
243243
"./vs/editor/contrib/suggest/suggest.ts",
244+
"./vs/editor/contrib/suggest/suggestAlternatives.ts",
244245
"./vs/editor/contrib/suggest/suggestMemory.ts",
246+
"./vs/editor/contrib/suggest/suggestWidget.ts",
245247
"./vs/editor/contrib/suggest/test/completionModel.test.ts",
246248
"./vs/editor/contrib/suggest/test/suggest.test.ts",
247249
"./vs/editor/contrib/suggest/wordContextKey.ts",

src/vs/editor/browser/editorBrowser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ export interface ICodeEditor extends editorCommon.IEditor {
682682
/**
683683
* @internal
684684
*/
685-
getTelemetryData(): { [key: string]: any; } | null;
685+
getTelemetryData(): { [key: string]: any } | undefined;
686686

687687
/**
688688
* Returns the editor's dom node

src/vs/editor/browser/widget/codeEditorWidget.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
198198
//#endregion
199199

200200
public readonly isSimpleWidget: boolean;
201-
private readonly _telemetryData: object | null;
201+
private readonly _telemetryData?: object;
202202

203203
private readonly _domElement: HTMLElement;
204204
private readonly _id: number;
@@ -245,7 +245,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
245245
this._decorationTypeKeysToIds = {};
246246
this._decorationTypeSubtypes = {};
247247
this.isSimpleWidget = codeEditorWidgetOptions.isSimpleWidget || false;
248-
this._telemetryData = codeEditorWidgetOptions.telemetryData || null;
248+
this._telemetryData = codeEditorWidgetOptions.telemetryData;
249249

250250
options = options || {};
251251
this._configuration = this._register(this._createConfiguration(options));
@@ -1507,7 +1507,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
15071507
/* __GDPR__FRAGMENT__
15081508
"EditorTelemetryData" : {}
15091509
*/
1510-
public getTelemetryData(): { [key: string]: any; } | null {
1510+
public getTelemetryData(): { [key: string]: any; } | undefined {
15111511
return this._telemetryData;
15121512
}
15131513

src/vs/editor/contrib/markdown/markdownRenderer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export class MarkdownRenderer {
7777
};
7878
}
7979

80-
render(markdown: IMarkdownString): IMarkdownRenderResult {
80+
render(markdown: IMarkdownString | undefined): IMarkdownRenderResult {
8181
let disposeables: IDisposable[] = [];
8282

8383
let element: HTMLElement;

src/vs/editor/contrib/suggest/suggestWidget.ts

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ export const editorSuggestWidgetHighlightForeground = registerColor('editorSugge
6363

6464

6565
const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i;
66-
function matchesColor(text: string) {
66+
function matchesColor(text: string): string | null {
6767
return text && text.match(colorRegExp) ? text : null;
6868
}
6969

70-
function canExpandCompletionItem(item: CompletionItem) {
70+
function canExpandCompletionItem(item: CompletionItem | null) {
7171
if (!item) {
7272
return false;
7373
}
@@ -156,24 +156,24 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
156156
matches: createMatches(element.score)
157157
};
158158

159-
let color: string;
160-
if (suggestion.kind === CompletionItemKind.Color && (color = matchesColor(suggestion.label) || typeof suggestion.documentation === 'string' && matchesColor(suggestion.documentation))) {
159+
let color: string | null = null;
160+
if (suggestion.kind === CompletionItemKind.Color && ((color = matchesColor(suggestion.label)) || typeof suggestion.documentation === 'string' && matchesColor(suggestion.documentation))) {
161161
// special logic for 'color' completion items
162162
data.icon.className = 'icon customcolor';
163163
data.colorspan.style.backgroundColor = color;
164164

165165
} else if (suggestion.kind === CompletionItemKind.File && this._themeService.getIconTheme().hasFileIcons) {
166166
// special logic for 'file' completion items
167167
data.icon.className = 'icon hide';
168-
labelOptions.extraClasses = [].concat(
168+
labelOptions.extraClasses = ([] as string[]).concat(
169169
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FILE),
170170
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FILE)
171171
);
172172

173173
} else if (suggestion.kind === CompletionItemKind.Folder && this._themeService.getIconTheme().hasFolderIcons) {
174174
// special logic for 'folder' completion items
175175
data.icon.className = 'icon hide';
176-
labelOptions.extraClasses = [].concat(
176+
labelOptions.extraClasses = ([] as string[]).concat(
177177
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FOLDER),
178178
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FOLDER)
179179
);
@@ -229,7 +229,7 @@ class SuggestionDetails {
229229
private header: HTMLElement;
230230
private type: HTMLElement;
231231
private docs: HTMLElement;
232-
private ariaLabel: string;
232+
private ariaLabel: string | null;
233233
private disposables: IDisposable[];
234234
private renderDisposeable: IDisposable;
235235
private borderWidth: number = 1;
@@ -324,7 +324,7 @@ class SuggestionDetails {
324324
item.completion.documentation ? (typeof item.completion.documentation === 'string' ? item.completion.documentation : item.completion.documentation.value) : '');
325325
}
326326

327-
getAriaLabel(): string {
327+
getAriaLabel() {
328328
return this.ariaLabel;
329329
}
330330

@@ -394,13 +394,13 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
394394
// Editor.IContentWidget.allowEditorOverflow
395395
readonly allowEditorOverflow = true;
396396

397-
private state: State;
397+
private state: State | null;
398398
private isAuto: boolean;
399399
private loadingTimeout: any;
400-
private currentSuggestionDetails: CancelablePromise<void>;
401-
private focusedItem: CompletionItem;
400+
private currentSuggestionDetails: CancelablePromise<void> | null;
401+
private focusedItem: CompletionItem | null;
402402
private ignoreFocusEvents = false;
403-
private completionModel: CompletionModel;
403+
private completionModel: CompletionModel | null;
404404

405405
private element: HTMLElement;
406406
private messageElement: HTMLElement;
@@ -436,7 +436,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
436436
private firstFocusInCurrentList: boolean = false;
437437

438438
private preferDocPositionTop: boolean = false;
439-
private docsPositionPreviousWidgetY: number;
439+
private docsPositionPreviousWidgetY: number | null;
440440

441441
constructor(
442442
private editor: ICodeEditor,
@@ -515,10 +515,15 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
515515
return;
516516
}
517517

518+
const completionModel = this.completionModel;
519+
if (!completionModel) {
520+
return;
521+
}
522+
518523
const item = e.elements[0];
519524
const index = e.indexes[0];
520525
item.resolve(CancellationToken.None).then(() => {
521-
this.onDidSelectEmitter.fire({ item, index, model: this.completionModel });
526+
this.onDidSelectEmitter.fire({ item, index, model: completionModel });
522527
alert(nls.localize('suggestionAriaAccepted', "{0}, accepted", item.completion.label));
523528
this.editor.focus();
524529
});
@@ -539,8 +544,8 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
539544
}
540545
}
541546

542-
private _lastAriaAlertLabel: string;
543-
private _ariaAlert(newAriaAlertLabel: string): void {
547+
private _lastAriaAlertLabel: string | null;
548+
private _ariaAlert(newAriaAlertLabel: string | null): void {
544549
if (this._lastAriaAlertLabel === newAriaAlertLabel) {
545550
return;
546551
}
@@ -587,6 +592,10 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
587592
return;
588593
}
589594

595+
if (!this.completionModel) {
596+
return;
597+
}
598+
590599
const item = e.elements[0];
591600
const index = e.indexes[0];
592601

@@ -861,10 +870,12 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
861870
}
862871
}
863872

864-
getFocusedItem(): ISelectedSuggestion {
873+
getFocusedItem(): ISelectedSuggestion | undefined {
865874
if (this.state !== State.Hidden
866875
&& this.state !== State.Empty
867-
&& this.state !== State.Loading) {
876+
&& this.state !== State.Loading
877+
&& this.completionModel
878+
) {
868879

869880
return {
870881
item: this.list.getFocusedElements()[0],
@@ -933,7 +944,6 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
933944
*/
934945
this.telemetryService.publicLog('suggestWidget:expandDetails', this.editor.getTelemetryData());
935946
}
936-
937947
}
938948

939949
showDetails(): void {
@@ -981,7 +991,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
981991
this.onDidHideEmitter.fire(this);
982992
}
983993

984-
getPosition(): IContentWidgetPosition {
994+
getPosition(): IContentWidgetPosition | null {
985995
if (this.state === State.Hidden) {
986996
return null;
987997
}
@@ -1025,6 +1035,10 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
10251035
* Adds the propert classes, margins when positioning the docs to the side
10261036
*/
10271037
private adjustDocsPosition() {
1038+
if (!this.editor.hasModel()) {
1039+
return;
1040+
}
1041+
10281042
const lineHeight = this.editor.getConfiguration().fontInfo.lineHeight;
10291043
const cursorCoords = this.editor.getScrolledVisiblePosition(this.editor.getPosition());
10301044
const editorCoords = getDomNodePagePosition(this.editor.getDomNode());
@@ -1075,7 +1089,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
10751089
return;
10761090
}
10771091

1078-
let matches = this.element.style.maxWidth.match(/(\d+)px/);
1092+
let matches = this.element.style.maxWidth!.match(/(\d+)px/);
10791093
if (!matches || Number(matches[1]) < this.maxWidgetWidth) {
10801094
addClass(this.element, 'docs-below');
10811095
removeClass(this.element, 'docs-side');
@@ -1118,13 +1132,13 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
11181132
this.state = null;
11191133
this.currentSuggestionDetails = null;
11201134
this.focusedItem = null;
1121-
this.element = null;
1122-
this.messageElement = null;
1123-
this.listElement = null;
1135+
this.element = null!; // StrictNullOverride: nulling out ok in dispose
1136+
this.messageElement = null!; // StrictNullOverride: nulling out ok in dispose
1137+
this.listElement = null!; // StrictNullOverride: nulling out ok in dispose
11241138
this.details.dispose();
1125-
this.details = null;
1139+
this.details = null!; // StrictNullOverride: nulling out ok in dispose
11261140
this.list.dispose();
1127-
this.list = null;
1141+
this.list = null!; // StrictNullOverride: nulling out ok in dispose
11281142
this.toDispose = dispose(this.toDispose);
11291143
if (this.loadingTimeout) {
11301144
clearTimeout(this.loadingTimeout);

0 commit comments

Comments
 (0)