-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enables strictNullChecks to breadcrumbs.ts, outlineModel.ts, breadcrumbsModel.ts #65062
Changes from 4 commits
c14ac10
9bbe60f
38d4a54
e48c4a2
e69d6ce
3b11ac4
760081d
beb8c6f
77710c1
06e4e22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,12 +20,14 @@ export abstract class TreeElement { | |
|
||
abstract id: string; | ||
abstract children: { [id: string]: TreeElement }; | ||
abstract parent: TreeElement; | ||
abstract parent: TreeElement | undefined; | ||
|
||
abstract adopt(newParent: TreeElement): TreeElement; | ||
|
||
remove(): void { | ||
delete this.parent.children[this.id]; | ||
if (this.parent) { | ||
delete this.parent.children[this.id]; | ||
} | ||
} | ||
|
||
static findId(candidate: DocumentSymbol | string, container: TreeElement): string { | ||
|
@@ -49,7 +51,7 @@ export abstract class TreeElement { | |
return id; | ||
} | ||
|
||
static getElementById(id: string, element: TreeElement): TreeElement { | ||
static getElementById(id: string, element: TreeElement): TreeElement | undefined { | ||
if (!id) { | ||
return undefined; | ||
} | ||
|
@@ -88,18 +90,18 @@ export abstract class TreeElement { | |
export class OutlineElement extends TreeElement { | ||
|
||
children: { [id: string]: OutlineElement; } = Object.create(null); | ||
score: FuzzyScore = FuzzyScore.Default; | ||
marker: { count: number, topSev: MarkerSeverity }; | ||
score: FuzzyScore | undefined = FuzzyScore.Default; | ||
marker: { count: number, topSev: MarkerSeverity } | undefined; | ||
|
||
constructor( | ||
readonly id: string, | ||
public parent: OutlineModel | OutlineGroup | OutlineElement, | ||
public parent: TreeElement | undefined, | ||
readonly symbol: DocumentSymbol | ||
) { | ||
super(); | ||
} | ||
|
||
adopt(parent: OutlineModel | OutlineGroup | OutlineElement): OutlineElement { | ||
adopt(parent: TreeElement): OutlineElement { | ||
let res = new OutlineElement(this.id, parent, this.symbol); | ||
forEach(this.children, entry => res.children[entry.key] = entry.value.adopt(res)); | ||
return res; | ||
|
@@ -112,33 +114,33 @@ export class OutlineGroup extends TreeElement { | |
|
||
constructor( | ||
readonly id: string, | ||
public parent: OutlineModel, | ||
public parent: TreeElement | undefined, | ||
readonly provider: DocumentSymbolProvider, | ||
readonly providerIndex: number, | ||
) { | ||
super(); | ||
} | ||
|
||
adopt(parent: OutlineModel): OutlineGroup { | ||
adopt(parent: TreeElement): OutlineGroup { | ||
let res = new OutlineGroup(this.id, parent, this.provider, this.providerIndex); | ||
forEach(this.children, entry => res.children[entry.key] = entry.value.adopt(res)); | ||
return res; | ||
} | ||
|
||
updateMatches(pattern: string, topMatch: OutlineElement): OutlineElement { | ||
updateMatches(pattern: string, topMatch: OutlineElement | undefined): OutlineElement | undefined { | ||
for (const key in this.children) { | ||
topMatch = this._updateMatches(pattern, this.children[key], topMatch); | ||
} | ||
return topMatch; | ||
} | ||
|
||
private _updateMatches(pattern: string, item: OutlineElement, topMatch: OutlineElement): OutlineElement { | ||
private _updateMatches(pattern: string, item: OutlineElement, topMatch: OutlineElement | undefined): OutlineElement | undefined { | ||
|
||
item.score = pattern | ||
? fuzzyScore(pattern, pattern.toLowerCase(), 0, item.symbol.name, item.symbol.name.toLowerCase(), 0, true) | ||
: FuzzyScore.Default; | ||
|
||
if (item.score && (!topMatch || item.score[0] > topMatch.score[0])) { | ||
if (item.score && (!topMatch || !topMatch.score || item.score[0] > topMatch.score[0])) { | ||
topMatch = item; | ||
} | ||
for (const key in item.children) { | ||
|
@@ -152,11 +154,11 @@ export class OutlineGroup extends TreeElement { | |
return topMatch; | ||
} | ||
|
||
getItemEnclosingPosition(position: IPosition): OutlineElement { | ||
getItemEnclosingPosition(position: IPosition): OutlineElement | undefined { | ||
return position ? this._getItemEnclosingPosition(position, this.children) : undefined; | ||
} | ||
|
||
private _getItemEnclosingPosition(position: IPosition, children: { [id: string]: OutlineElement }): OutlineElement { | ||
private _getItemEnclosingPosition(position: IPosition, children: { [id: string]: OutlineElement }): OutlineElement | undefined { | ||
for (let key in children) { | ||
let item = children[key]; | ||
if (!item.symbol.range || !Range.containsPosition(item.symbol.range, position)) { | ||
|
@@ -174,6 +176,7 @@ export class OutlineGroup extends TreeElement { | |
} | ||
|
||
private _updateMarker(markers: IMarker[], item: OutlineElement): void { | ||
let filteredMarkers: Array<IMarker | undefined> = markers; | ||
|
||
item.marker = undefined; | ||
|
||
|
@@ -190,14 +193,14 @@ export class OutlineGroup extends TreeElement { | |
} | ||
|
||
let myMarkers: IMarker[] = []; | ||
let myTopSev: MarkerSeverity; | ||
let myTopSev: MarkerSeverity | undefined = undefined; | ||
jrieken marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
for (; start < markers.length && Range.areIntersecting(item.symbol.range, markers[start]); start++) { | ||
// remove markers intersecting with this outline element | ||
// and store them in a 'private' array. | ||
let marker = markers[start]; | ||
myMarkers.push(marker); | ||
markers[start] = undefined; | ||
filteredMarkers[start] = undefined; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's important to modify There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue is that I tried changing the type of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when in doubt always use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right that makes perfect sense, I'm just not sure how to apply it in this case because the What do you think of this other idea? Use type casting: |
||
if (!myTopSev || marker.severity > myTopSev) { | ||
myTopSev = marker.severity; | ||
} | ||
|
@@ -218,13 +221,13 @@ export class OutlineGroup extends TreeElement { | |
}; | ||
} | ||
|
||
coalesceInPlace(markers); | ||
coalesceInPlace(filteredMarkers); | ||
} | ||
} | ||
|
||
export class OutlineModel extends TreeElement { | ||
|
||
private static readonly _requests = new LRUCache<string, { promiseCnt: number, source: CancellationTokenSource, promise: Promise<any>, model: OutlineModel }>(9, .75); | ||
private static readonly _requests = new LRUCache<string, { promiseCnt: number, source: CancellationTokenSource, promise: Promise<any>, model: OutlineModel | undefined }>(9, .75); | ||
private static readonly _keys = new class { | ||
|
||
private _counter = 1; | ||
|
@@ -265,25 +268,25 @@ export class OutlineModel extends TreeElement { | |
OutlineModel._requests.set(key, data); | ||
} | ||
|
||
if (data.model) { | ||
if (data!.model) { | ||
// resolved -> return data | ||
return Promise.resolve(data.model); | ||
} | ||
|
||
// increase usage counter | ||
data.promiseCnt += 1; | ||
data!.promiseCnt += 1; | ||
|
||
token.onCancellationRequested(() => { | ||
// last -> cancel provider request, remove cached promise | ||
if (--data.promiseCnt === 0) { | ||
data.source.cancel(); | ||
if (--data!.promiseCnt === 0) { | ||
data!.source.cancel(); | ||
OutlineModel._requests.delete(key); | ||
} | ||
}); | ||
|
||
return new Promise((resolve, reject) => { | ||
data.promise.then(model => { | ||
data.model = model; | ||
data!.promise.then(model => { | ||
data!.model = model; | ||
resolve(model); | ||
}, err => { | ||
OutlineModel._requests.delete(key); | ||
|
@@ -331,7 +334,7 @@ export class OutlineModel extends TreeElement { | |
container.children[res.id] = res; | ||
} | ||
|
||
static get(element: TreeElement): OutlineModel { | ||
static get(element: TreeElement | undefined): OutlineModel | undefined { | ||
while (element) { | ||
if (element instanceof OutlineModel) { | ||
return element; | ||
|
@@ -358,26 +361,22 @@ export class OutlineModel extends TreeElement { | |
} | ||
|
||
private _compact(): this { | ||
let count = 0; | ||
for (const key in this._groups) { | ||
let group = this._groups[key]; | ||
if (first(group.children) === undefined) { // empty | ||
delete this._groups[key]; | ||
} else { | ||
count += 1; | ||
} | ||
} | ||
if (count !== 1) { | ||
// | ||
this.children = this._groups; | ||
} else { | ||
let group = first(this._groups); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch -- I think I misread the intent of the code and though I could get away with checking if |
||
if (group) { | ||
// adopt all elements of the first group | ||
let group = first(this._groups); | ||
for (let key in group.children) { | ||
let child = group.children[key]; | ||
child.parent = this; | ||
this.children[child.id] = child; | ||
} | ||
} else { | ||
this.children = this._groups; | ||
} | ||
return this; | ||
} | ||
|
@@ -394,23 +393,23 @@ export class OutlineModel extends TreeElement { | |
return true; | ||
} | ||
|
||
private _matches: [string, OutlineElement]; | ||
private _matches: [string, OutlineElement | undefined]; | ||
|
||
updateMatches(pattern: string): OutlineElement { | ||
updateMatches(pattern: string): OutlineElement | undefined { | ||
if (this._matches && this._matches[0] === pattern) { | ||
return this._matches[1]; | ||
} | ||
let topMatch: OutlineElement; | ||
let topMatch: OutlineElement | undefined; | ||
for (const key in this._groups) { | ||
topMatch = this._groups[key].updateMatches(pattern, topMatch); | ||
} | ||
this._matches = [pattern, topMatch]; | ||
return topMatch; | ||
} | ||
|
||
getItemEnclosingPosition(position: IPosition, context?: OutlineElement): OutlineElement { | ||
getItemEnclosingPosition(position: IPosition, context?: OutlineElement): OutlineElement | undefined { | ||
|
||
let preferredGroup: OutlineGroup; | ||
let preferredGroup: OutlineGroup | undefined; | ||
if (context) { | ||
let candidate = context.parent; | ||
while (candidate && !preferredGroup) { | ||
|
@@ -432,7 +431,7 @@ export class OutlineModel extends TreeElement { | |
return result; | ||
} | ||
|
||
getItemById(id: string): TreeElement { | ||
getItemById(id: string): TreeElement | undefined { | ||
return TreeElement.getElementById(id, this); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -90,10 +90,18 @@ export abstract class BreadcrumbsConfig<T> { | |
readonly name = name; | ||
readonly onDidChange = onDidChange.event; | ||
getValue(overrides?: IConfigurationOverrides): T { | ||
return service.getValue(name, overrides); | ||
if (overrides) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sandy081 is that really needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I get
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see.. looks like the API is strict here to not to accept @jrieken up to you. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please relax @sandy081 |
||
return service.getValue(name, overrides); | ||
} else { | ||
return service.getValue(name); | ||
} | ||
} | ||
updateValue(newValue: T, overrides?: IConfigurationOverrides): Promise<void> { | ||
return service.updateValue(name, newValue, overrides); | ||
if (overrides) { | ||
return service.updateValue(name, newValue, overrides); | ||
} else { | ||
return service.updateValue(name, newValue); | ||
} | ||
} | ||
dispose(): void { | ||
listener.dispose(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,7 @@ export class FileElement { | |
|
||
export type BreadcrumbElement = FileElement | OutlineModel | OutlineGroup | OutlineElement; | ||
|
||
type FileInfo = { path: FileElement[], folder: IWorkspaceFolder }; | ||
type FileInfo = { path: FileElement[], folder?: IWorkspaceFolder }; | ||
|
||
export class EditorBreadcrumbsModel { | ||
|
||
|
@@ -105,16 +105,17 @@ export class EditorBreadcrumbsModel { | |
} | ||
|
||
let info: FileInfo = { | ||
folder: workspaceService.getWorkspaceFolder(uri), | ||
folder: workspaceService.getWorkspaceFolder(uri) || undefined, | ||
path: [] | ||
}; | ||
|
||
while (uri.path !== '/') { | ||
if (info.folder && isEqual(info.folder.uri, uri)) { | ||
let uriPrefix: URI | null = uri; | ||
while (uriPrefix && uriPrefix.path !== '/') { | ||
if (info.folder && isEqual(info.folder.uri, uriPrefix)) { | ||
break; | ||
} | ||
info.path.unshift(new FileElement(uri, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER)); | ||
uri = dirname(uri); | ||
info.path.unshift(new FileElement(uriPrefix, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER)); | ||
uriPrefix = dirname(uriPrefix); | ||
} | ||
|
||
if (info.folder && workspaceService.getWorkbenchState() === WorkbenchState.WORKSPACE) { | ||
|
@@ -145,7 +146,12 @@ export class EditorBreadcrumbsModel { | |
this._updateOutlineElements([]); | ||
} | ||
|
||
const buffer = this._editor.getModel(); | ||
const editor = this._editor; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
if (!editor) { | ||
return; | ||
} | ||
|
||
const buffer = editor.getModel(); | ||
if (!buffer || !DocumentSymbolProviderRegistry.has(buffer) || !isEqual(buffer.uri, this._uri)) { | ||
return; | ||
} | ||
|
@@ -171,11 +177,11 @@ export class EditorBreadcrumbsModel { | |
// copy the model | ||
model = model.adopt(); | ||
|
||
this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); | ||
this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => { | ||
this._updateOutlineElements(this._getOutlineElements(model, editor.getPosition())); | ||
this._outlineDisposables.push(editor.onDidChangeCursorPosition(_ => { | ||
timeout.cancelAndSet(() => { | ||
if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId() && this._editor.getModel()) { | ||
this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); | ||
if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId() && editor.getModel()) { | ||
this._updateOutlineElements(this._getOutlineElements(model, editor.getPosition())); | ||
} | ||
}, 150); | ||
})); | ||
|
@@ -186,11 +192,11 @@ export class EditorBreadcrumbsModel { | |
}); | ||
} | ||
|
||
private _getOutlineElements(model: OutlineModel, position: IPosition): Array<OutlineModel | OutlineGroup | OutlineElement> { | ||
if (!model) { | ||
private _getOutlineElements(model: OutlineModel, position: IPosition | null): Array<OutlineModel | OutlineGroup | OutlineElement> { | ||
if (!model || !position) { | ||
return []; | ||
} | ||
let item: OutlineGroup | OutlineElement = model.getItemEnclosingPosition(position); | ||
let item: OutlineGroup | OutlineElement | undefined = model.getItemEnclosingPosition(position); | ||
if (!item) { | ||
return [model]; | ||
} | ||
|
@@ -201,7 +207,7 @@ export class EditorBreadcrumbsModel { | |
if (parent instanceof OutlineModel) { | ||
break; | ||
} | ||
if (parent instanceof OutlineGroup && size(parent.parent.children) === 1) { | ||
if (parent instanceof OutlineGroup && parent.parent && size(parent.parent.children) === 1) { | ||
break; | ||
} | ||
item = parent; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please refrain from making changes aren't needed for strict null checks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was surprised and spent quite a while trying to understand why, but this use of inheritance did cause errors under strict null checks. You can see them by checking out commit
9bbe60f
(I separated the 'normal' null error fixes from the inheritance-related ones in separate commits).I'm not 100% confident in my understand of this error but I think the error prevents a hypothetical error that could be make like this:
There's no such code right now, of course, but the type system doesn't care. Happy to learn if there's a better way to silence these errors though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, intersting. Again, in doubt try to use the
!
operator. My challenge is that it's hard to reason about these things but that I know that the code in its current shape runs. So, I'd prefer to not really change itThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand, but this just changes the type annotation from
OutlineModel | OutlineGroup | OutlineElement
to their parentTreeElement
, so I think it's safe? See 38d4a54