Strict null scripting (#12126)

* strict null scripting

* fix compile

* fix tests

* fix icon
This commit is contained in:
Anthony Dresser
2020-09-04 18:04:08 -07:00
committed by GitHub
parent bbe5b98a2c
commit 503090856a
35 changed files with 427 additions and 403 deletions

View File

@@ -9,7 +9,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati
import Severity from 'vs/base/common/severity';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { ISelectionEvent, ITree } from 'vs/base/parts/tree/browser/tree';
import { Disposable } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -29,31 +29,30 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { Button } from 'sql/base/browser/ui/button/button';
import { attachButtonStyler } from 'sql/platform/theme/common/styler';
import { TreeNode, TreeItemCollapsibleState } from 'sql/workbench/services/objectExplorer/common/treeNode';
import { SERVER_GROUP_AUTOEXPAND_CONFIG } from 'sql/workbench/contrib/objectExplorer/common/serverGroup.contribution';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
import { ServerTreeActionProvider } from 'sql/workbench/services/objectExplorer/browser/serverTreeActionProvider';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { isHidden } from 'sql/base/browser/dom';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { startsWith } from 'vs/base/common/strings';
import { SERVER_GROUP_CONFIG } from 'sql/workbench/services/serverGroup/common/interfaces';
import { horizontalScrollingKey } from 'vs/platform/list/browser/listService';
import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
import { ITreeContextMenuEvent, ITreeEvent } from 'vs/base/browser/ui/tree/tree';
import { ObjectExplorerActionsContext } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
import { coalesce } from 'vs/base/common/arrays';
/**
* ServerTreeview implements the dynamic tree view.
*/
export class ServerTreeView extends Disposable implements IServerTreeView {
public messages: HTMLElement;
private _buttonSection: HTMLElement;
public messages?: HTMLElement;
private _buttonSection?: HTMLElement;
private _treeSelectionHandler: TreeSelectionHandler;
private _activeConnectionsFilterAction: ActiveConnectionsFilterAction;
private _tree: ITree | AsyncServerTree;
private _tree?: ITree | AsyncServerTree;
private _onSelectionOrFocusChange: Emitter<void>;
private _actionProvider: ServerTreeActionProvider;
@@ -81,7 +80,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (this._tree instanceof AsyncServerTree) {
// Refresh the tree input now that the capabilities are registered so that we can
// get the full ConnectionProfiles with the server info updated properly
const treeInput = TreeUpdateUtils.getTreeInput(this._connectionManagementService);
const treeInput = TreeUpdateUtils.getTreeInput(this._connectionManagementService)!;
await this._tree.setInput(treeInput);
this._treeSelectionHandler.onTreeActionStateChange(false);
} else {
@@ -113,7 +112,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
}
public get tree(): ITree | AsyncServerTree {
return this._tree;
return this._tree!;
}
/**
@@ -159,14 +158,14 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
const horizontalScrollEnabled: boolean = this._configurationService.getValue(horizontalScrollingKey) || false;
this._tree = this._register(TreeCreationUtils.createServersTree(container, this._instantiationService, this._configurationService, horizontalScrollEnabled));
this._register(this._tree.onDidChangeSelection((event) => this.onSelected(event)));
this._register(this._tree.onDidChangeSelection((event: ISelectionEvent | ITreeEvent<ServerTreeElement>) => this.onSelected(event)));
this._register(this._tree.onDidBlur(() => this._onSelectionOrFocusChange.fire()));
this._register(this._tree.onDidChangeFocus(() => this._onSelectionOrFocusChange.fire()));
if (this._tree instanceof AsyncServerTree) {
this._register(this._tree.onContextMenu(e => this.onContextMenu(e)));
this._register(this._tree.onMouseDblClick(e => {
// Open dashboard on double click for server and database nodes
let connectionProfile: ConnectionProfile;
let connectionProfile: ConnectionProfile | undefined;
if (e.element instanceof ConnectionProfile) {
connectionProfile = e.element;
} else if (e.element instanceof TreeNode) {
@@ -212,16 +211,16 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
return new Promise<void>(async (resolve, reject) => {
await this.refreshTree();
const root = <ConnectionProfileGroup>this._tree.getInput();
const root = <ConnectionProfileGroup>this._tree!.getInput();
const expandGroups: boolean = this._configurationService.getValue(SERVER_GROUP_CONFIG)[SERVER_GROUP_AUTOEXPAND_CONFIG];
const expandGroups = this._configurationService.getValue<{ autoExpand: boolean }>(SERVER_GROUP_CONFIG).autoExpand;
if (expandGroups) {
if (this._tree instanceof AsyncServerTree) {
await Promise.all(ConnectionProfileGroup.getSubgroups(root).map(subgroup => {
return this._tree.expand(subgroup);
return this._tree!.expand(subgroup);
}));
} else {
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(root));
await this._tree!.expandAll(ConnectionProfileGroup.getSubgroups(root));
}
}
@@ -238,7 +237,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
public isObjectExplorerConnectionUri(uri: string): boolean {
let isBackupRestoreUri: boolean = uri.indexOf(ConnectionUtils.ConnectionUriBackupIdAttributeName) >= 0 ||
uri.indexOf(ConnectionUtils.ConnectionUriRestoreIdAttributeName) >= 0;
return uri && startsWith(uri, ConnectionUtils.uriPrefixes.default) && !isBackupRestoreUri;
return !!uri && uri.startsWith(ConnectionUtils.uriPrefixes.default) && !isBackupRestoreUri;
}
private async handleAddConnectionProfile(newProfile?: IConnectionProfile): Promise<void> {
@@ -277,16 +276,16 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
}
}
const currentSelections = this._tree.getSelection();
const currentSelections = this._tree!.getSelection();
const currentSelectedElement = currentSelections && currentSelections.length >= 1 ? currentSelections[0] : undefined;
const newProfileIsSelected = currentSelectedElement && newProfile ? currentSelectedElement.id === newProfile.id : false;
if (newProfile && currentSelectedElement && !newProfileIsSelected) {
this._tree.clearSelection();
this._tree!.clearSelection();
}
await this.refreshTree();
if (newProfile && !newProfileIsSelected) {
await this._tree.reveal(newProfile);
this._tree.select(newProfile);
await this._tree!.reveal(newProfile);
this._tree!.select(newProfile);
}
}
@@ -304,11 +303,11 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
*/
private getConnectionInTreeInput(connectionId: string): ConnectionProfile | undefined {
if (this._tree instanceof AsyncServerTree) {
const root = this._tree.getInput();
const root = this._tree.getInput()!;
const connections = ConnectionProfileGroup.getConnectionsInGroup(root);
return connections.find(conn => conn.id === connectionId);
} else {
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService);
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService)!;
const connections = ConnectionProfileGroup.getConnectionsInGroup(root);
const results = connections.filter(con => {
if (connectionId === con.id) {
@@ -330,16 +329,16 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (this._tree instanceof AsyncServerTree) {
this._tree.rerender(element);
} else {
await this._tree.refresh(element);
await this._tree!.refresh(element);
}
await this._tree.expand(element);
await this._tree.reveal(element, 0.5);
await this._tree!.expand(element);
await this._tree!.reveal(element, 0.5);
this._treeSelectionHandler.onTreeActionStateChange(false);
}
}
public addObjectExplorerNodeAndRefreshTree(connection: IConnectionProfile): void {
hide(this.messages);
hide(this.messages!);
if (!this._objectExplorerService.getObjectExplorerNode(connection)) {
this._objectExplorerService.updateObjectExplorerNodes(connection).catch(e => errors.onUnexpectedError(e));
}
@@ -356,35 +355,35 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
this._tree.collapse(conn);
await this.refreshTree();
} else {
await this._tree.collapse(conn);
return this._tree.refresh(conn);
await this._tree!.collapse(conn);
return this._tree!.refresh(conn);
}
}
}
}
public async refreshTree(): Promise<void> {
hide(this.messages);
hide(this.messages!);
this.clearOtherActions();
return TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService);
return TreeUpdateUtils.registeredServerUpdate(this._tree!, this._connectionManagementService);
}
public async refreshElement(element: ServerTreeElement): Promise<void> {
if (this._tree instanceof AsyncServerTree) {
return this._tree.updateChildren(element);
} else {
return this._tree.refresh(element);
return this._tree!.refresh(element);
}
}
/**
* Filter connections based on view (recent/active)
*/
private filterConnections(treeInput: ConnectionProfileGroup[], view: string): ConnectionProfileGroup[] {
private filterConnections(treeInput: ConnectionProfileGroup[] | undefined, view: string): ConnectionProfileGroup[] | undefined {
if (!treeInput || treeInput.length === 0) {
return undefined;
}
const result = treeInput.map(group => {
const result = coalesce(treeInput.map(group => {
// Keep active/recent connections and remove the rest
if (group.connections) {
group.connections = group.connections.filter(con => {
@@ -408,7 +407,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
return group;
}
return undefined;
});
}));
return result;
}
@@ -416,35 +415,35 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
* Set tree elements based on the view (recent/active)
*/
public showFilteredTree(view: string): void {
hide(this.messages);
hide(this.messages!);
// Clear other action views if user switched between two views
this.clearOtherActions(view);
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService);
let treeInput: ConnectionProfileGroup = null;
let treeInput: ConnectionProfileGroup | undefined = undefined;
if (root) {
// Filter results based on view
const filteredResults = this.filterConnections([root], view);
if (!filteredResults || !filteredResults[0]) {
show(this.messages);
this.messages.focus();
show(this.messages!);
this.messages!.focus();
} else {
treeInput = filteredResults[0];
}
this._tree.setInput(treeInput).then(async () => {
if (isHidden(this.messages)) {
this._tree.getFocus();
this._tree!.setInput(treeInput!).then(async () => {
if (isHidden(this.messages!)) {
this._tree!.getFocus();
if (this._tree instanceof AsyncServerTree) {
await Promise.all(ConnectionProfileGroup.getSubgroups(treeInput).map(subgroup => {
this._tree.expand(subgroup);
await Promise.all(ConnectionProfileGroup.getSubgroups(treeInput!).map(subgroup => {
this._tree!.expand(subgroup);
}));
} else {
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput));
await this._tree!.expandAll(ConnectionProfileGroup.getSubgroups(treeInput!));
}
} else {
if (this._tree instanceof AsyncServerTree) {
this._tree.setFocus([]);
} else {
this._tree.clearFocus();
this._tree!.clearFocus();
}
}
}, errors.onUnexpectedError);
@@ -460,33 +459,33 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (!searchString) {
return;
}
hide(this.messages);
hide(this.messages!);
// Clear other actions if user searched during other views
this.clearOtherActions();
// Filter connections based on search
const filteredResults = this.searchConnections(searchString);
if (!filteredResults || filteredResults.length === 0) {
show(this.messages);
this.messages.focus();
show(this.messages!);
this.messages!.focus();
}
// Add all connections to tree root and set tree input
const treeInput = new ConnectionProfileGroup('searchroot', undefined, 'searchroot', undefined, undefined);
treeInput.addConnections(filteredResults);
this._tree.setInput(treeInput).then(async () => {
if (isHidden(this.messages)) {
this._tree.getFocus();
this._tree!.setInput(treeInput).then(async () => {
if (isHidden(this.messages!)) {
this._tree!.getFocus();
if (this._tree instanceof AsyncServerTree) {
await Promise.all(ConnectionProfileGroup.getSubgroups(treeInput).map(subgroup => {
this._tree.expand(subgroup);
this._tree!.expand(subgroup);
}));
} else {
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput));
await this._tree!.expandAll(ConnectionProfileGroup.getSubgroups(treeInput));
}
} else {
if (this._tree instanceof AsyncServerTree) {
this._tree.setFocus([]);
} else {
this._tree.clearFocus();
this._tree!.clearFocus();
}
}
}, errors.onUnexpectedError);
@@ -497,7 +496,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
*/
private searchConnections(searchString: string): ConnectionProfile[] {
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService);
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService)!;
const connections = ConnectionProfileGroup.getConnectionsInGroup(root);
const results = connections.filter(con => {
if (searchString && (searchString.length > 0)) {
@@ -542,7 +541,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
}
private onSelected(event: any): void {
this._treeSelectionHandler.onTreeSelect(event, this._tree, this._connectionManagementService, this._objectExplorerService, () => this._onSelectionOrFocusChange.fire());
this._treeSelectionHandler.onTreeSelect(event, this._tree!, this._connectionManagementService, this._objectExplorerService, () => this._onSelectionOrFocusChange.fire());
this._onSelectionOrFocusChange.fire();
}
@@ -550,21 +549,21 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
* set the layout of the view
*/
public layout(height: number): void {
this._tree.layout(height);
this._tree!.layout(height);
}
/**
* Get the list of selected nodes in the tree
*/
public getSelection(): any[] {
return this._tree.getSelection();
return this._tree!.getSelection();
}
/**
* Get whether the tree view currently has focus
*/
public isFocused(): boolean {
return this._tree.getHTMLElement() === document.activeElement;
return this._tree!.getHTMLElement() === document.activeElement;
}
/**
@@ -572,9 +571,9 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
*/
public async setExpandedState(element: ServerTreeElement, expandedState?: TreeItemCollapsibleState): Promise<void> {
if (expandedState === TreeItemCollapsibleState.Collapsed) {
return this._tree.collapse(element);
return this._tree!.collapse(element);
} else if (expandedState === TreeItemCollapsibleState.Expanded) {
return this._tree.expand(element);
return this._tree!.expand(element);
}
}
@@ -582,7 +581,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
* Reveal the given element in the tree
*/
public async reveal(element: ServerTreeElement): Promise<void> {
return this._tree.reveal(element);
return this._tree!.reveal(element);
}
/**
@@ -593,7 +592,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (this._tree instanceof AsyncServerTree) {
this._tree.setSelection([]);
} else {
this._tree.clearSelection();
this._tree!.clearSelection();
}
}
@@ -602,14 +601,14 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
this._tree.setSelection(this._tree.getSelection().concat(element));
this._tree.reveal(element);
} else {
this._tree.select(element);
return this._tree.reveal(element);
this._tree!.select(element);
return this._tree!.reveal(element);
}
} else {
if (this._tree instanceof AsyncServerTree) {
this._tree.setSelection(this._tree.getSelection().filter(item => item !== element));
} else {
this._tree.deselect(element);
this._tree!.deselect(element);
}
}
}
@@ -621,7 +620,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (this._tree instanceof AsyncServerTree) {
return !this._tree.getNode(element).collapsed;
} else {
return this._tree.isExpanded(element);
return this._tree!.isExpanded(element);
}
}
@@ -630,42 +629,45 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
* Return actions in the context menu
*/
private onContextMenu(e: ITreeContextMenuEvent<ServerTreeElement>): boolean {
e.browserEvent.preventDefault();
e.browserEvent.stopPropagation();
this._tree.setSelection([e.element]);
if (e.element) {
e.browserEvent.preventDefault();
e.browserEvent.stopPropagation();
this._tree!.setSelection([e.element]);
let actionContext: any;
if (e.element instanceof TreeNode) {
let context = new ObjectExplorerActionsContext();
context.nodeInfo = e.element.toNodeInfo();
// Note: getting DB name before, but intentionally not using treeUpdateUtils.getConnectionProfile as it replaces
// the connection ID with a new one. This breaks a number of internal tasks
context.connectionProfile = e.element.getConnectionProfile().toIConnectionProfile();
context.connectionProfile.databaseName = e.element.getDatabaseName();
actionContext = context;
} else if (e.element instanceof ConnectionProfile) {
let context = new ObjectExplorerActionsContext();
context.connectionProfile = e.element.toIConnectionProfile();
context.isConnectionNode = true;
actionContext = context;
} else {
// TODO: because the connection group is used as a context object and isn't serializable,
// the Group-level context menu is not currently extensible
actionContext = e.element;
let actionContext: any;
if (e.element instanceof TreeNode) {
let context = new ObjectExplorerActionsContext();
context.nodeInfo = e.element.toNodeInfo();
// Note: getting DB name before, but intentionally not using treeUpdateUtils.getConnectionProfile as it replaces
// the connection ID with a new one. This breaks a number of internal tasks
context.connectionProfile = e.element.getConnectionProfile()!.toIConnectionProfile();
context.connectionProfile.databaseName = e.element.getDatabaseName();
actionContext = context;
} else if (e.element instanceof ConnectionProfile) {
let context = new ObjectExplorerActionsContext();
context.connectionProfile = e.element.toIConnectionProfile();
context.isConnectionNode = true;
actionContext = context;
} else {
// TODO: because the connection group is used as a context object and isn't serializable,
// the Group-level context menu is not currently extensible
actionContext = e.element;
}
this._contextMenuService.showContextMenu({
getAnchor: () => e.anchor,
getActions: () => this._actionProvider.getActions(this._tree!, e.element!),
getKeyBinding: (action) => this._keybindingService.lookupKeybinding(action.id),
onHide: (wasCancelled?: boolean) => {
if (wasCancelled) {
this._tree!.domFocus();
}
},
getActionsContext: () => (actionContext)
});
return true;
}
this._contextMenuService.showContextMenu({
getAnchor: () => e.anchor,
getActions: () => this._actionProvider.getActions(this._tree, e.element),
getKeyBinding: (action) => this._keybindingService.lookupKeybinding(action.id),
onHide: (wasCancelled?: boolean) => {
if (wasCancelled) {
this._tree.domFocus();
}
},
getActionsContext: () => (actionContext)
});
return true;
return false;
}
}