diff --git a/src/sql/parts/objectExplorer/viewlet/serverTreeActionProvider.ts b/src/sql/parts/objectExplorer/viewlet/serverTreeActionProvider.ts index 2b1ce5cac6..6839ad2588 100644 --- a/src/sql/parts/objectExplorer/viewlet/serverTreeActionProvider.ts +++ b/src/sql/parts/objectExplorer/viewlet/serverTreeActionProvider.ts @@ -80,10 +80,6 @@ export class ServerTreeActionProvider extends ContributableActionProvider { return false; } - public getSecondaryActions(tree: ITree, element: any): IAction[] { - return super.getSecondaryActions(tree, element); - } - /** * Return actions for connection elements */ diff --git a/src/sql/parts/taskHistory/viewlet/taskHistoryActionProvider.ts b/src/sql/parts/taskHistory/viewlet/taskHistoryActionProvider.ts index e2a97e62fd..378af9ec64 100644 --- a/src/sql/parts/taskHistory/viewlet/taskHistoryActionProvider.ts +++ b/src/sql/parts/taskHistory/viewlet/taskHistoryActionProvider.ts @@ -40,10 +40,6 @@ export class TaskHistoryActionProvider extends ContributableActionProvider { return false; } - public getSecondaryActions(tree: ITree, element: any): IAction[] { - return super.getSecondaryActions(tree, element); - } - /** * Return actions for history task */ @@ -64,4 +60,4 @@ export class TaskHistoryActionProvider extends ContributableActionProvider { return actions; } -} \ No newline at end of file +} diff --git a/src/typings/lib.ie11_safe_es6.d.ts b/src/typings/lib.ie11_safe_es6.d.ts index 43bde6f7c6..9b86ad1383 100644 --- a/src/typings/lib.ie11_safe_es6.d.ts +++ b/src/typings/lib.ie11_safe_es6.d.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ // Defined a subset of ES6 built ins that run in IE11 diff --git a/src/typings/spdlog.d.ts b/src/typings/spdlog.d.ts index 086915f9cf..3b84ffd6dc 100644 --- a/src/typings/spdlog.d.ts +++ b/src/typings/spdlog.d.ts @@ -6,7 +6,7 @@ declare module 'spdlog' { export const version: string; - export function setAsyncMode(bufferSize: number, flushInterval: number); + export function setAsyncMode(bufferSize: number, flushInterval: number): void; export enum LogLevel { CRITICAL, @@ -21,14 +21,14 @@ declare module 'spdlog' { export class RotatingLogger { constructor(name: string, filename: string, filesize: number, filecount: number); - trace(message: string); - debug(message: string); - info(message: string); - warn(message: string); - error(message: string); - critical(message: string); - setLevel(level: number); - clearFormatters(); + trace(message: string): void; + debug(message: string): void; + info(message: string): void; + warn(message: string): void; + error(message: string): void; + critical(message: string): void; + setLevel(level: number): void; + clearFormatters(): void; /** * A synchronous operation to flush the contents into file */ diff --git a/src/typings/sudo-prompt.d.ts b/src/typings/sudo-prompt.d.ts index 04ed40aa5e..9149efa19a 100644 --- a/src/typings/sudo-prompt.d.ts +++ b/src/typings/sudo-prompt.d.ts @@ -5,5 +5,5 @@ declare module 'sudo-prompt' { - export function exec(cmd: string, options: { name?: string, icns?: string }, callback: (error: string, stdout: string, stderr: string) => void); + export function exec(cmd: string, options: { name?: string, icns?: string }, callback: (error: string, stdout: string, stderr: string) => void): void; } \ No newline at end of file diff --git a/src/typings/yauzl.d.ts b/src/typings/yauzl.d.ts index cf8310351a..3a26286d60 100644 --- a/src/typings/yauzl.d.ts +++ b/src/typings/yauzl.d.ts @@ -31,9 +31,9 @@ declare module 'yauzl' { } export class ZipFile extends EventEmitter { - readEntry(); - openReadStream(entry: Entry, callback: (err?: Error, stream?: Readable) => void); - close(); + readEntry(): void; + openReadStream(entry: Entry, callback: (err?: Error, stream?: Readable) => void): void; + close(): void; isOpen: boolean; entryCount: number; comment: string; diff --git a/src/typings/yazl.d.ts b/src/typings/yazl.d.ts index 5ddf6aa92c..172f8d66ad 100644 --- a/src/typings/yazl.d.ts +++ b/src/typings/yazl.d.ts @@ -8,8 +8,8 @@ declare module 'yazl' { class ZipFile { outputStream: stream.Stream; - addBuffer(buffer: Buffer, path: string); - addFile(localPath: string, path: string); - end(); + addBuffer(buffer: Buffer, path: string): void; + addFile(localPath: string, path: string): void; + end(): void; } } \ No newline at end of file diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index 46b2b90207..557f16c86a 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -25,7 +25,7 @@ export class ContextSubMenu extends SubmenuAction { export interface IContextMenuDelegate { getAnchor(): HTMLElement | { x: number; y: number; width?: number; height?: number; }; getActions(): Array; - getActionItem?(action: IAction): IActionItem | null; + getActionItem?(action: IAction): IActionItem | undefined; getActionsContext?(event?: IContextMenuEvent): any; getKeyBinding?(action: IAction): ResolvedKeybinding | undefined; getMenuClassName?(): string; diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index a2d60485ab..947e04dc72 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -364,7 +364,7 @@ export interface ActionTrigger { } export interface IActionItemProvider { - (action: IAction): IActionItem | null; + (action: IAction): IActionItem | undefined; } export interface IActionBarOptions { @@ -609,7 +609,7 @@ export class ActionBar extends Disposable implements IActionRunner { e.stopPropagation(); })); - let item: IActionItem | null = null; + let item: IActionItem | undefined; if (this.options.actionItemProvider) { item = this.options.actionItemProvider(action); diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index ea3781a3eb..8d460ca778 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -118,7 +118,7 @@ export class ContextView extends Disposable { this._register(toDisposable(() => this.setContainer(null))); } - public setContainer(container: HTMLElement | null): void { + setContainer(container: HTMLElement | null): void { if (this.container) { this.toDisposeOnSetContainer = dispose(this.toDisposeOnSetContainer); this.container.removeChild(this.view); @@ -146,7 +146,7 @@ export class ContextView extends Disposable { } } - public show(delegate: IDelegate): void { + show(delegate: IDelegate): void { if (this.isVisible()) { this.hide(); } @@ -173,7 +173,7 @@ export class ContextView extends Disposable { } } - public layout(): void { + layout(): void { if (!this.isVisible()) { return; } @@ -258,7 +258,7 @@ export class ContextView extends Disposable { this.view.style.width = 'initial'; } - public hide(data?: any): void { + hide(data?: any): void { if (this.delegate && this.delegate.onHide) { this.delegate.onHide(data); } @@ -288,7 +288,7 @@ export class ContextView extends Disposable { } } - public dispose(): void { + dispose(): void { this.hide(); super.dispose(); diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index fc6f536fa5..64e9625dfa 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -247,7 +247,7 @@ export class DropdownMenu extends BaseDropdown { getAnchor: () => this.element, getActions: () => this.actions, getActionsContext: () => this.menuOptions ? this.menuOptions.context : null, - getActionItem: action => this.menuOptions && this.menuOptions.actionItemProvider ? this.menuOptions.actionItemProvider(action) : null, + getActionItem: action => this.menuOptions && this.menuOptions.actionItemProvider ? this.menuOptions.actionItemProvider(action) : undefined, getKeyBinding: action => this.menuOptions && this.menuOptions.getKeyBinding ? this.menuOptions.getKeyBinding(action) : undefined, getMenuClassName: () => this.menuClassName, onHide: () => this.onHide(), diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index a6e52c0531..008ad0b6d1 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -63,7 +63,7 @@ const DefaultOptions = { setRowLineHeight: true, supportDynamicHeights: false, dnd: { - getDragElements(e) { return [e]; }, + getDragElements(e: T) { return [e]; }, getDragURI() { return null; }, onDragStart(): void { }, onDragOver() { return false; }, diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 3ae2320d90..1ed2b7fc74 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -860,8 +860,8 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { onDidModelSplice({ insertedNodes, deletedNodes }: ITreeModelSpliceEvent): void { if (!this.identityProvider) { const set = this.createNodeSet(); - const visit = node => set.delete(node); + const visit = (node: ITreeNode) => set.delete(node); deletedNodes.forEach(node => dfs(node, visit)); this.set(values(set)); return; @@ -816,8 +816,8 @@ class Trait { this.nodes.forEach(node => nodesByIdentity.set(identityProvider.getId(node.element).toString(), node)); const toDeleteByIdentity = new Map>(); - const toRemoveSetter = node => toDeleteByIdentity.set(identityProvider.getId(node.element).toString(), node); - const toRemoveDeleter = node => toDeleteByIdentity.delete(identityProvider.getId(node.element).toString()); + const toRemoveSetter = (node: ITreeNode) => toDeleteByIdentity.set(identityProvider.getId(node.element).toString(), node); + const toRemoveDeleter = (node: { element: T; }) => toDeleteByIdentity.delete(identityProvider.getId(node.element).toString()); deletedNodes.forEach(node => dfs(node, toRemoveSetter)); insertedNodes.forEach(node => dfs(node, toRemoveDeleter)); diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 51a7fb8ec2..6fe80936f3 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -16,6 +16,7 @@ import { IDragAndDropData } from 'vs/base/browser/dnd'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; import { toggleClass } from 'vs/base/browser/dom'; +import { values } from 'vs/base/common/map'; interface IAsyncDataTreeNode { element: TInput | T; @@ -26,7 +27,6 @@ interface IAsyncDataTreeNode { hasChildren: boolean; stale: boolean; slow: boolean; - disposed: boolean; } interface IAsyncDataTreeNodeRequiredProps extends Partial> { @@ -41,7 +41,6 @@ function createAsyncDataTreeNode(props: IAsyncDataTreeNodeRequiredPro children: [], loading: false, stale: true, - disposed: false, slow: false }; } @@ -274,6 +273,11 @@ interface IAsyncDataTreeViewStateContext { readonly focus: IAsyncDataTreeNode[]; } +function dfs(node: IAsyncDataTreeNode, fn: (node: IAsyncDataTreeNode) => void): void { + fn(node); + node.children.forEach(child => dfs(child, fn)); +} + export class AsyncDataTree implements IDisposable { private readonly tree: ObjectTree, TFilterData>; @@ -629,11 +633,6 @@ export class AsyncDataTree implements IDisposable } private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { - if (node.disposed) { - console.error('Async data tree node is disposed'); - return; - } - let result: Promise | undefined; this.subTreeRefreshPromises.forEach((refreshPromise, refreshNode) => { @@ -748,37 +747,47 @@ export class AsyncDataTree implements IDisposable return []; } - let nodeChildren: Map> | undefined; + const nodesToForget = new Map>(); + const childrenTreeNodesById = new Map | null, TFilterData>>(); - if (this.identityProvider) { - nodeChildren = new Map(); + for (const child of node.children) { + nodesToForget.set(child.element as T, child); - for (const child of node.children) { - nodeChildren.set(child.id!, child); + if (this.identityProvider) { + childrenTreeNodesById.set(child.id!, this.tree.getNode(child)); } } - let childrenToRefresh: IAsyncDataTreeNode[] = []; + const childrenToRefresh: IAsyncDataTreeNode[] = []; const children = childrenElements.map>(element => { if (!this.identityProvider) { return createAsyncDataTreeNode({ element, parent: node, - hasChildren: !!this.dataSource.hasChildren(element), + hasChildren: !!this.dataSource.hasChildren(element) }); } const id = this.identityProvider.getId(element).toString(); - const asyncDataTreeNode = nodeChildren!.get(id); + const childNode = childrenTreeNodesById.get(id); + + if (childNode) { + const asyncDataTreeNode = childNode.element!; + + nodesToForget.delete(asyncDataTreeNode.element as T); + this.nodes.delete(asyncDataTreeNode.element as T); + this.nodes.set(element, asyncDataTreeNode); - if (asyncDataTreeNode) { asyncDataTreeNode.element = element; - asyncDataTreeNode.stale = asyncDataTreeNode.stale || recursive; asyncDataTreeNode.hasChildren = !!this.dataSource.hasChildren(element); - if (recursive && !this.tree.isCollapsed(asyncDataTreeNode)) { - childrenToRefresh.push(asyncDataTreeNode); + if (recursive) { + if (childNode.collapsed) { + dfs(asyncDataTreeNode, node => node.stale = true); + } else { + childrenToRefresh.push(asyncDataTreeNode); + } } return asyncDataTreeNode; @@ -806,33 +815,22 @@ export class AsyncDataTree implements IDisposable return childAsyncDataTreeNode; }); + for (const node of values(nodesToForget)) { + dfs(node, node => this.nodes.delete(node.element as T)); + } + + for (const child of children) { + this.nodes.set(child.element as T, child); + } + node.children.splice(0, node.children.length, ...children); return childrenToRefresh; } private render(node: IAsyncDataTreeNode, viewStateContext?: IAsyncDataTreeViewStateContext): void { - const insertedElements = new Set(); - - const onDidCreateNode = (treeNode: ITreeNode, TFilterData>) => { - if (treeNode.element.element) { - insertedElements.add(treeNode.element.element as T); - this.nodes.set(treeNode.element.element as T, treeNode.element); - } - }; - - const onDidDeleteNode = (treeNode: ITreeNode, TFilterData>) => { - if (treeNode.element.element) { - if (!insertedElements.has(treeNode.element.element as T)) { - treeNode.element.disposed = true; - this.nodes.delete(treeNode.element.element as T); - } - } - }; - const children = node.children.map(c => asTreeElement(c, viewStateContext)); - this.tree.setChildren(node === this.root ? null : node, children, onDidCreateNode, onDidDeleteNode); - + this.tree.setChildren(node === this.root ? null : node, children); this._onDidRender.fire(); } diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index d0c590656c..c597490189 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -198,8 +198,17 @@ export class ObjectTreeModel, TFilterData extends Non } getNode(element: T | null = null): ITreeNode { - const location = this.getElementLocation(element); - return this.model.getNode(location); + if (element === null) { + return this.model.getNode(this.model.rootRef); + } + + const node = this.nodes.get(element); + + if (!node) { + throw new Error(`Tree element not found: ${element}`); + } + + return node; } getNodeLocation(node: ITreeNode): T { diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 54fb690450..d0897e31b4 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -404,9 +404,8 @@ export function firstIndex(array: ReadonlyArray, fn: (item: T) => boolean) } export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T): T; -export function first(array: ReadonlyArray, fn: (item: T) => boolean): T | null; -export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T | null): T | null; -export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T | null = null): T | null { +export function first(array: ReadonlyArray, fn: (item: T) => boolean): T | undefined; +export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T | undefined = undefined): T | undefined { const index = firstIndex(array, fn); return index < 0 ? notFoundValue : array[index]; } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index dec35a7ffb..2d7ca15f58 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -178,7 +178,7 @@ export class Delayer implements IDisposable { private timeout: any; private completionPromise: Promise | null; private doResolve: ((value?: any | Promise) => void) | null; - private doReject: (err: any) => void; + private doReject?: (err: any) => void; private task: ITask> | null; constructor(public defaultDelay: number) { @@ -222,7 +222,7 @@ export class Delayer implements IDisposable { this.cancelTimeout(); if (this.completionPromise) { - this.doReject(errors.canceled()); + this.doReject!(errors.canceled()); this.completionPromise = null; } } @@ -282,7 +282,7 @@ export class Barrier { private _isOpen: boolean; private _promise: Promise; - private _completePromise: (v: boolean) => void; + private _completePromise!: (v: boolean) => void; constructor() { this._isOpen = false; @@ -731,8 +731,8 @@ export class IdleValue { private readonly _executor: () => void; private readonly _handle: IDisposable; - private _didRun: boolean; - private _value: T; + private _didRun: boolean = false; + private _value?: T; private _error: any; constructor(executor: () => T) { @@ -760,7 +760,7 @@ export class IdleValue { if (this._error) { throw this._error; } - return this._value; + return this._value!; } } diff --git a/src/vs/base/common/cancellation.ts b/src/vs/base/common/cancellation.ts index f160d44357..56b740fb6f 100644 --- a/src/vs/base/common/cancellation.ts +++ b/src/vs/base/common/cancellation.ts @@ -87,7 +87,7 @@ class MutableToken implements CancellationToken { export class CancellationTokenSource { - private _token: CancellationToken; + private _token?: CancellationToken; get token(): CancellationToken { if (!this._token) { diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index 6b50c2665e..4013db5d53 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -260,7 +260,7 @@ export class Color { } readonly rgba: RGBA; - private _hsla: HSLA; + private _hsla?: HSLA; get hsla(): HSLA { if (this._hsla) { return this._hsla; @@ -269,7 +269,7 @@ export class Color { } } - private _hsva: HSVA; + private _hsva?: HSVA; get hsva(): HSVA { if (this._hsva) { return this._hsva; diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 8c74239f88..6aca2ec198 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -119,6 +119,15 @@ function isWhitespace(code: number): boolean { ); } +const wordSeparators = new Set(); +'`~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?' + .split('') + .forEach(s => wordSeparators.add(s.charCodeAt(0))); + +function isWordSeparator(code: number): boolean { + return wordSeparators.has(code); +} + function isAlphanumeric(code: number): boolean { return isLower(code) || isUpper(code) || isNumber(code); } @@ -308,7 +317,8 @@ function _matchesWords(word: string, target: string, i: number, j: number, conti function nextWord(word: string, start: number): number { for (let i = start; i < word.length; i++) { const c = word.charCodeAt(i); - if (isWhitespace(c) || (i > 0 && isWhitespace(word.charCodeAt(i - 1)))) { + if (isWhitespace(c) || (i > 0 && isWhitespace(word.charCodeAt(i - 1))) || + isWordSeparator(c) || (i > 0 && isWordSeparator(word.charCodeAt(i - 1)))) { return i; } } diff --git a/src/vs/base/common/path.ts b/src/vs/base/common/path.ts index 31f4b32538..483746bd3e 100644 --- a/src/vs/base/common/path.ts +++ b/src/vs/base/common/path.ts @@ -43,7 +43,7 @@ const CHAR_QUESTION_MARK = 63; /* ? */ class ErrorInvalidArgType extends Error { code: 'ERR_INVALID_ARG_TYPE'; - constructor(name: string, expected: string, actual: string) { + constructor(name: string, expected: string, actual: any) { // determiner: 'must be' or 'must not be' let determiner; if (typeof expected === 'string' && expected.indexOf('not ') === 0) { @@ -53,36 +53,35 @@ class ErrorInvalidArgType extends Error { determiner = 'must be'; } - let msg; const type = name.indexOf('.') !== -1 ? 'property' : 'argument'; - msg = `The "${name}" ${type} ${determiner} of type ${expected}`; + let msg = `The "${name}" ${type} ${determiner} of type ${expected}`; msg += `. Received type ${typeof actual}`; super(msg); } } -function validateString(value: string, name) { +function validateString(value: string, name: string) { if (typeof value !== 'string') { throw new ErrorInvalidArgType(name, 'string', value); } } -function isPathSeparator(code) { +function isPathSeparator(code: number) { return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; } -function isPosixPathSeparator(code) { +function isPosixPathSeparator(code: number) { return code === CHAR_FORWARD_SLASH; } -function isWindowsDeviceRoot(code) { +function isWindowsDeviceRoot(code: number) { return code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z || code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z; } // Resolves . and .. elements in a path with directory names -function normalizeString(path, allowAboveRoot, separator, isPathSeparator) { +function normalizeString(path: string, allowAboveRoot: boolean, separator: string, isPathSeparator: (code?: number) => boolean) { let res = ''; let lastSegmentLength = 0; let lastSlash = -1; @@ -155,7 +154,7 @@ function normalizeString(path, allowAboveRoot, separator, isPathSeparator) { return res; } -function _format(sep, pathObject) { +function _format(sep: string, pathObject: ParsedPath) { const dir = pathObject.dir || pathObject.root; const base = pathObject.base || ((pathObject.name || '') + (pathObject.ext || '')); @@ -185,7 +184,7 @@ interface IPath { dirname(path: string): string; basename(path: string, ext?: string): string; extname(path: string): string; - format(pathObject): string; + format(pathObject: ParsedPath): string; parse(path: string): ParsedPath; toNamespacedPath(path: string): string; sep: '\\' | '/'; @@ -501,7 +500,7 @@ export const win32: IPath = { } let joined; - let firstPart; + let firstPart: string | undefined; for (let i = 0; i < paths.length; ++i) { const arg = paths[i]; validateString(arg, 'path'); @@ -534,7 +533,7 @@ export const win32: IPath = { // path.join('//server', 'share') -> '\\\\server\\share\\') let needsReplace = true; let slashCount = 0; - if (isPathSeparator(firstPart.charCodeAt(0))) { + if (typeof firstPart === 'string' && isPathSeparator(firstPart.charCodeAt(0))) { ++slashCount; const firstLen = firstPart.length; if (firstLen > 1) { diff --git a/src/vs/base/common/scrollable.ts b/src/vs/base/common/scrollable.ts index 7e1b33d2d3..d36295a6c9 100644 --- a/src/vs/base/common/scrollable.ts +++ b/src/vs/base/common/scrollable.ts @@ -377,8 +377,8 @@ export class SmoothScrollingOperation { private readonly _startTime: number; public animationFrameDisposable: IDisposable | null; - private scrollLeft: IAnimation; - private scrollTop: IAnimation; + private scrollLeft!: IAnimation; + private scrollTop!: IAnimation; protected constructor(from: ISmoothScrollPosition, to: ISmoothScrollPosition, startTime: number, duration: number) { this.from = from; diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 8a52b22f58..a9667e174c 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -174,7 +174,7 @@ export function create(ctor: Function, ...args: any[]): any { } // https://stackoverflow.com/a/32235645/1499159 -function isNativeClass(thing): boolean { +function isNativeClass(thing: any): boolean { return typeof thing === 'function' && thing.hasOwnProperty('prototype') && !thing.hasOwnProperty('arguments'); diff --git a/src/vs/base/node/id.ts b/src/vs/base/node/id.ts index 15f59928d9..8649aadfe8 100644 --- a/src/vs/base/node/id.ts +++ b/src/vs/base/node/id.ts @@ -20,8 +20,8 @@ import { TernarySearchTree } from 'vs/base/common/map'; // Sun xVM VirtualBox 08-00-27 export const virtualMachineHint: { value(): number } = new class { - private _virtualMachineOUIs: TernarySearchTree; - private _value: number; + private _virtualMachineOUIs?: TernarySearchTree; + private _value?: number; private _isVirtualMachineMacAdress(mac: string): boolean { if (!this._virtualMachineOUIs) { diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index e72ea26d4e..fc176f9d95 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -59,6 +59,10 @@ export function lstat(path: string): Promise { return nfcall(fs.lstat, path); } +export function move(oldPath: string, newPath: string): Promise { + return nfcall(extfs.mv, oldPath, newPath); +} + export function rename(oldPath: string, newPath: string): Promise { return nfcall(fs.rename, oldPath, newPath); } diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 894d810aa1..b97e428182 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree'; import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen'; -import { Action, IAction, IActionRunner, IActionItem } from 'vs/base/common/actions'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; import { compareAnything } from 'vs/base/common/comparers'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; @@ -291,18 +291,6 @@ class NoActionProvider implements IActionProvider { getActions(tree: ITree, element: any): IAction[] | null { return null; } - - hasSecondaryActions(tree: ITree, element: any): boolean { - return false; - } - - getSecondaryActions(tree: ITree, element: any): IAction[] | null { - return null; - } - - getActionItem(tree: ITree, element: any, action: Action): IActionItem | null { - return null; - } } export interface IQuickOpenEntryTemplateData { diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index bf4501dd91..453d98c62e 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -9,7 +9,7 @@ import * as Keyboard from 'vs/base/browser/keyboardEvent'; import { INavigator } from 'vs/base/common/iterator'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { Event } from 'vs/base/common/event'; -import { IAction, IActionItem } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { Color } from 'vs/base/common/color'; import { IItemCollapseEvent, IItemExpandEvent } from 'vs/base/parts/tree/browser/treeModel'; import { IDragAndDropData } from 'vs/base/browser/dnd'; @@ -169,81 +169,21 @@ export interface ITree { */ getHighlight(includeHidden?: boolean): any; - /** - * Returns whether an element is highlighted or not. - */ - isHighlighted(element: any): boolean; - /** * Clears the highlight. */ clearHighlight(eventPayload?: any): void; - /** - * Selects an element. - */ - select(element: any, eventPayload?: any): void; - - /** - * Selects a range of elements. - */ - selectRange(fromElement: any, toElement: any, eventPayload?: any): void; - - /** - * Deselects a range of elements. - */ - deselectRange(fromElement: any, toElement: any, eventPayload?: any): void; - - /** - * Selects several elements. - */ - selectAll(elements: any[], eventPayload?: any): void; - - /** - * Deselects an element. - */ - deselect(element: any, eventPayload?: any): void; - - /** - * Deselects several elements. - */ - deselectAll(elements: any[], eventPayload?: any): void; - /** * Replaces the current selection with the given elements. */ setSelection(elements: any[], eventPayload?: any): void; - /** - * Toggles the element's selection. - */ - toggleSelection(element: any, eventPayload?: any): void; - /** * Returns the currently selected elements. */ getSelection(includeHidden?: boolean): any[]; - /** - * Returns whether an element is selected or not. - */ - isSelected(element: any): boolean; - - /** - * Selects the next `count`-nth element, in visible order. - */ - selectNext(count?: number, clearSelection?: boolean, eventPayload?: any): void; - - /** - * Selects the previous `count`-nth element, in visible order. - */ - selectPrevious(count?: number, clearSelection?: boolean, eventPayload?: any): void; - - /** - * Selects the currently selected element's parent. - */ - selectParent(clearSelection?: boolean, eventPayload?: any): void; - /** * Clears the selection. */ @@ -254,11 +194,6 @@ export interface ITree { */ setFocus(element?: any, eventPayload?: any): void; - /** - * Returns whether an element is focused or not. - */ - isFocused(element: any): boolean; - /** * Returns focused element. */ @@ -316,6 +251,7 @@ export interface ITree { */ clearFocus(eventPayload?: any): void; + // {{SQL CARBON EDIT}} @todo anthonydresser we need to refactor our code to not need these methods /** * Adds the trait to elements. */ @@ -327,14 +263,15 @@ export interface ITree { removeTraits(trait: string, elements: any[]): void; /** - * Toggles the element's trait. + * Selects an element. */ - toggleTrait(trait: string, element: any): void; + select(element: any, eventPayload?: any): void; /** - * Returns whether the element has the trait or not. + * Deselects an element. */ - hasTrait(trait: string, element: any): boolean; + deselect(element: any, eventPayload?: any): void; + // {{SQL CARBON EDIT}} END /** * Returns a navigator which allows to discover the visible and @@ -582,12 +519,14 @@ export interface IDragOverReaction { autoExpand?: boolean; } +// {{SQL CARBON EDIT}} @todo anthonydresser refactor to not need this export const DRAG_OVER_REJECT: IDragOverReaction = { accept: false }; export const DRAG_OVER_ACCEPT: IDragOverReaction = { accept: true }; export const DRAG_OVER_ACCEPT_BUBBLE_UP: IDragOverReaction = { accept: true, bubble: DragOverBubble.BUBBLE_UP }; export const DRAG_OVER_ACCEPT_BUBBLE_DOWN = (autoExpand = false) => ({ accept: true, bubble: DragOverBubble.BUBBLE_DOWN, autoExpand }); export const DRAG_OVER_ACCEPT_BUBBLE_UP_COPY: IDragOverReaction = { accept: true, bubble: DragOverBubble.BUBBLE_UP, effect: DragOverEffect.COPY }; export const DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY = (autoExpand = false) => ({ accept: true, bubble: DragOverBubble.BUBBLE_DOWN, effect: DragOverEffect.COPY, autoExpand }); +// {{SQL CARBON EDIT}} END export interface IDragAndDrop { @@ -635,12 +574,6 @@ export interface IFilter { isVisible(tree: ITree, element: any): boolean; } -export interface IElementCallback { - (tree: ITree, element: any): void; -} - -export type ICallback = () => void; - export interface ISorter { /** @@ -730,19 +663,4 @@ export interface IActionProvider { * Returns a promise of an array with the actions of the element that should show up in place right to the element in the tree. */ getActions(tree: ITree | null, element: any): IAction[] | null; - - /** - * Returns whether or not the element has secondary actions. These show up once the user has expanded the element's action bar. - */ - hasSecondaryActions(tree: ITree, element: any): boolean; - - /** - * Returns a promise of an array with the secondary actions of the element that should show up once the user has expanded the element's action bar. - */ - getSecondaryActions(tree: ITree, element: any): IAction[] | null; - - /** - * Returns an action item to render an action. - */ - getActionItem(tree: ITree, element: any, action: IAction): IActionItem | null; } diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts index c2482813ac..817fca8b22 100644 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ b/src/vs/base/parts/tree/browser/treeImpl.ts @@ -230,50 +230,14 @@ export class Tree implements _.ITree { return this.model.getHighlight(); } - public isHighlighted(element: any): boolean { - return this.model.isFocused(element); - } - public clearHighlight(eventPayload?: any): void { this.model.setHighlight(null, eventPayload); } - public select(element: any, eventPayload?: any): void { - this.model.select(element, eventPayload); - } - - public selectRange(fromElement: any, toElement: any, eventPayload?: any): void { - this.model.selectRange(fromElement, toElement, eventPayload); - } - - public deselectRange(fromElement: any, toElement: any, eventPayload?: any): void { - this.model.deselectRange(fromElement, toElement, eventPayload); - } - - public selectAll(elements: any[], eventPayload?: any): void { - this.model.selectAll(elements, eventPayload); - } - - public deselect(element: any, eventPayload?: any): void { - this.model.deselect(element, eventPayload); - } - - public deselectAll(elements: any[], eventPayload?: any): void { - this.model.deselectAll(elements, eventPayload); - } - public setSelection(elements: any[], eventPayload?: any): void { this.model.setSelection(elements, eventPayload); } - public toggleSelection(element: any, eventPayload?: any): void { - this.model.toggleSelection(element, eventPayload); - } - - public isSelected(element: any): boolean { - return this.model.isSelected(element); - } - public getSelection(): any[] { return this.model.getSelection(); } @@ -282,26 +246,10 @@ export class Tree implements _.ITree { this.model.setSelection([], eventPayload); } - public selectNext(count?: number, clearSelection?: boolean, eventPayload?: any): void { - this.model.selectNext(count, clearSelection, eventPayload); - } - - public selectPrevious(count?: number, clearSelection?: boolean, eventPayload?: any): void { - this.model.selectPrevious(count, clearSelection, eventPayload); - } - - public selectParent(clearSelection?: boolean, eventPayload?: any): void { - this.model.selectParent(clearSelection, eventPayload); - } - public setFocus(element?: any, eventPayload?: any): void { this.model.setFocus(element, eventPayload); } - public isFocused(element: any): boolean { - return this.model.isFocused(element); - } - public getFocus(): any { return this.model.getFocus(); } @@ -346,6 +294,7 @@ export class Tree implements _.ITree { this.model.setFocus(null, eventPayload); } + // {{SQL CARBON EDIT}} @todo anthonydresser we need to refactor our code to not need these methods public addTraits(trait: string, elements: any[]): void { this.model.addTraits(trait, elements); } @@ -354,14 +303,14 @@ export class Tree implements _.ITree { this.model.removeTraits(trait, elements); } - public toggleTrait(trait: string, element: any): void { - this.model.hasTrait(trait, element) ? this.model.removeTraits(trait, [element]) - : this.model.addTraits(trait, [element]); + public select(element: any, eventPayload?: any): void { + this.model.select(element, eventPayload); } - public hasTrait(trait: string, element: any): boolean { - return this.model.hasTrait(trait, element); + public deselect(element: any, eventPayload?: any): void { + this.model.deselect(element, eventPayload); } + // {{SQL CARBON EDIT}} end getNavigator(fromElement?: any, subTreeOnly?: boolean): INavigator { return new MappedNavigator(this.model.getNavigator(fromElement, subTreeOnly), i => i && i.getElement()); diff --git a/src/vs/base/parts/tree/browser/treeModel.ts b/src/vs/base/parts/tree/browser/treeModel.ts index 5226fd60be..9bb249181c 100644 --- a/src/vs/base/parts/tree/browser/treeModel.ts +++ b/src/vs/base/parts/tree/browser/treeModel.ts @@ -6,7 +6,6 @@ import * as Assert from 'vs/base/common/assert'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import * as arrays from 'vs/base/common/arrays'; import { INavigator } from 'vs/base/common/iterator'; import * as _ from './tree'; import { Event, Emitter, EventMultiplexer, Relay } from 'vs/base/common/event'; @@ -872,42 +871,6 @@ export class TreeNavigator implements INavigator { } } -function getRange(one: Item, other: Item): Item[] { - let oneHierarchy = one.getHierarchy(); - let otherHierarchy = other.getHierarchy(); - let length = arrays.commonPrefixLength(oneHierarchy, otherHierarchy); - let item: Item | null = oneHierarchy[length - 1]; - let nav = item.getNavigator(); - - let oneIndex: number | null = null; - let otherIndex: number | null = null; - - let index = 0; - let result: Item[] = []; - - while (item && (oneIndex === null || otherIndex === null)) { - result.push(item); - - if (item === one) { - oneIndex = index; - } - if (item === other) { - otherIndex = index; - } - - index++; - item = nav.next(); - } - - if (oneIndex === null || otherIndex === null) { - return []; - } - - let min = Math.min(oneIndex, otherIndex); - let max = Math.max(oneIndex, otherIndex); - return result.slice(min, max + 1); -} - export interface IBaseEvent { item: Item | null; } @@ -1205,28 +1168,6 @@ export class TreeModel { this.selectAll([element], eventPayload); } - public selectRange(fromElement: any, toElement: any, eventPayload?: any): void { - let fromItem = this.getItem(fromElement); - let toItem = this.getItem(toElement); - - if (!fromItem || !toItem) { - return; - } - - this.selectAll(getRange(fromItem, toItem), eventPayload); - } - - public deselectRange(fromElement: any, toElement: any, eventPayload?: any): void { - let fromItem = this.getItem(fromElement); - let toItem = this.getItem(toElement); - - if (!fromItem || !toItem) { - return; - } - - this.deselectAll(getRange(fromItem, toItem), eventPayload); - } - public selectAll(elements: any[], eventPayload?: any): void { this.addTraits('selected', elements); let eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; @@ -1249,12 +1190,6 @@ export class TreeModel { this._onDidSelect.fire(eventData); } - public toggleSelection(element: any, eventPayload?: any): void { - this.toggleTrait('selected', element); - let eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; - this._onDidSelect.fire(eventData); - } - public isSelected(element: any): boolean { let item = this.getItem(element); @@ -1324,21 +1259,6 @@ export class TreeModel { } } - public selectParent(eventPayload?: any, clearSelection: boolean = true): void { - let selection = this.getSelection(); - let item: Item = selection.length > 0 ? selection[0] : this.input; - let nav = this.getNavigator(item, false); - let parent = nav.parent(); - - if (parent) { - if (clearSelection) { - this.setSelection([parent], eventPayload); - } else { - this.select(parent, eventPayload); - } - } - } - public setFocus(element?: any, eventPayload?: any): void { this.setTraits('focused', element ? [element] : []); let eventData: _.IFocusEvent = { focus: this.getFocus(), payload: eventPayload }; @@ -1513,25 +1433,6 @@ export class TreeModel { } } - public hasTrait(trait: string, element: any): boolean { - const item = this.getItem(element); - return !!(item && item.hasTrait(trait)); - } - - private toggleTrait(trait: string, element: any): void { - let item = this.getItem(element); - - if (!item) { - return; - } - - if (item.hasTrait(trait)) { - this.removeTraits(trait, [element]); - } else { - this.addTraits(trait, [element]); - } - } - private setTraits(trait: string, elements: any[]): void { if (elements.length === 0) { this.removeTraits(trait, elements); diff --git a/src/vs/base/parts/tree/test/browser/treeModel.test.ts b/src/vs/base/parts/tree/test/browser/treeModel.test.ts index 24bdd29cfa..49de21e6a6 100644 --- a/src/vs/base/parts/tree/test/browser/treeModel.test.ts +++ b/src/vs/base/parts/tree/test/browser/treeModel.test.ts @@ -161,19 +161,19 @@ const SAMPLE: any = { }; class TestDataSource implements _.IDataSource { - public getId(tree, element): string { + public getId(tree: _.ITree, element: any): string { return element.id; } - public hasChildren(tree, element): boolean { + public hasChildren(tree: _.ITree, element: any): boolean { return !!element.children; } - public getChildren(tree, element): Promise { + public getChildren(tree: _.ITree, element: any): Promise { return Promise.resolve(element.children); } - public getParent(tree, element): Promise { + public getParent(tree: _.ITree, element: any): Promise { throw new Error('Not implemented'); } } @@ -700,13 +700,13 @@ suite('TreeModel - Expansion', () => { class TestFilter implements _.IFilter { - public fn: (any) => boolean; + public fn: (element: any) => boolean; constructor() { this.fn = () => true; } - public isVisible(tree, element): boolean { + public isVisible(tree: _.ITree, element: any): boolean { return this.fn(element); } } @@ -1092,39 +1092,39 @@ class DynamicModel implements _.IDataSource { this.promiseFactory = null; } - public addChild(parent, child): void { + public addChild(parent: string, child: string): void { if (!this.data[parent]) { this.data[parent] = []; } this.data[parent].push(child); } - public removeChild(parent, child): void { + public removeChild(parent: string, child: string): void { this.data[parent].splice(this.data[parent].indexOf(child), 1); if (this.data[parent].length === 0) { delete this.data[parent]; } } - public move(element, oldParent, newParent): void { + public move(element: string, oldParent: string, newParent: string): void { this.removeChild(oldParent, element); this.addChild(newParent, element); } - public rename(parent, oldName, newName): void { + public rename(parent: string, oldName: string, newName: string): void { this.removeChild(parent, oldName); this.addChild(parent, newName); } - public getId(tree, element): string { + public getId(tree: _.ITree, element: any): string { return element; } - public hasChildren(tree, element): boolean { + public hasChildren(tree: _.ITree, element: any): boolean { return !!this.data[element]; } - public getChildren(tree, element): Promise { + public getChildren(tree: _.ITree, element: any): Promise { this._onGetChildren.fire(element); const result = this.promiseFactory ? this.promiseFactory() : Promise.resolve(null); return result.then(() => { @@ -1133,7 +1133,7 @@ class DynamicModel implements _.IDataSource { }); } - public getParent(tree, element): Promise { + public getParent(tree: _.ITree, element: any): Promise { throw new Error('Not implemented'); } } @@ -1395,7 +1395,7 @@ suite('TreeModel - Dynamic data model', () => { assert.equal(getTimes, 2); assert.equal(gotTimes, 1); - let p2Complete; + let p2Complete: () => void; dataModel.promiseFactory = () => { return new Promise((c) => { p2Complete = c; }); }; const p2 = model.refresh('father'); @@ -1412,7 +1412,7 @@ suite('TreeModel - Dynamic data model', () => { assert.equal(getTimes, 3); assert.equal(gotTimes, 2); - p2Complete(); + p2Complete!(); // all good assert.equal(refreshTimes, 5); // (+1) second son request diff --git a/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts b/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts index 8b6d96f24f..541660bea9 100644 --- a/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts +++ b/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { ArrayIterator } from 'vs/base/common/iterator'; import { HeightMap, IViewItem } from 'vs/base/parts/tree/browser/treeViewModel'; -function makeItem(id, height): any { +function makeItem(id: any, height: any): any { return { id: id, getHeight: function () { return height; }, diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index fc44ff1e33..1748a32c33 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -59,7 +59,7 @@ class TestView implements IView { } function getSashes(splitview: SplitView): Sash[] { - return (splitview as any).sashItems.map(i => i.sash) as Sash[]; + return (splitview as any).sashItems.map((i: any) => i.sash) as Sash[]; } suite('Splitview', () => { diff --git a/src/vs/base/test/common/path.test.ts b/src/vs/base/test/common/path.test.ts index 98da358556..4007dfa3b7 100644 --- a/src/vs/base/test/common/path.test.ts +++ b/src/vs/base/test/common/path.test.ts @@ -143,12 +143,12 @@ suite('Paths (Node Implementation)', () => { ] ) ]); - joinTests.forEach((test) => { + joinTests.forEach((test: any[]) => { if (!Array.isArray(test[0])) { test[0] = [test[0]]; } - test[0].forEach((join) => { - test[1].forEach((test) => { + test[0].forEach((join: any) => { + test[1].forEach((test: any) => { const actual = join.apply(null, test[0]); const expected = test[1]; // For non-Windows specific tests with the Windows join(), we need to try diff --git a/src/vs/base/test/common/types.test.ts b/src/vs/base/test/common/types.test.ts index 6448015772..b243131f9e 100644 --- a/src/vs/base/test/common/types.test.ts +++ b/src/vs/base/test/common/types.test.ts @@ -176,14 +176,14 @@ suite('Types', () => { types.validateConstraints([undefined], [types.isUndefined]); types.validateConstraints([1], [types.isNumber]); - function foo() { } - types.validateConstraints([new foo()], [foo]); + class Foo { } + types.validateConstraints([new Foo()], [Foo]); - function isFoo(f) { } - assert.throws(() => types.validateConstraints([new foo()], [isFoo])); + function isFoo(f: any) { } + assert.throws(() => types.validateConstraints([new Foo()], [isFoo])); - function isFoo2(f) { return true; } - types.validateConstraints([new foo()], [isFoo2]); + function isFoo2(f: any) { return true; } + types.validateConstraints([new Foo()], [isFoo2]); assert.throws(() => types.validateConstraints([1, true], [types.isNumber, types.isString])); assert.throws(() => types.validateConstraints(['2'], [types.isNumber])); @@ -196,7 +196,7 @@ suite('Types', () => { assert(types.create(zeroConstructor) instanceof zeroConstructor); assert(types.isObject(types.create(zeroConstructor))); - let manyArgConstructor = function (this: any, foo, bar) { + let manyArgConstructor = function (this: any, foo: any, bar: any) { this.foo = foo; this.bar = bar; }; diff --git a/src/vs/base/test/node/encoding/encoding.test.ts b/src/vs/base/test/node/encoding/encoding.test.ts index a1d24c6a13..b8187fe5ab 100644 --- a/src/vs/base/test/node/encoding/encoding.test.ts +++ b/src/vs/base/test/node/encoding/encoding.test.ts @@ -132,13 +132,13 @@ suite('Encoding', () => { assert.equal(mimes.encoding, 'windows1252'); }); - async function readAndDecodeFromDisk(path, _encoding) { + async function readAndDecodeFromDisk(path: string, fileEncoding: string | null) { return new Promise((resolve, reject) => { fs.readFile(path, (err, data) => { if (err) { reject(err); } else { - resolve(encoding.decode(data, _encoding)); + resolve(encoding.decode(data, fileEncoding!)); } }); }); diff --git a/src/vs/base/test/node/extfs/extfs.test.ts b/src/vs/base/test/node/extfs/extfs.test.ts index a86d239dfa..74d0adbb71 100644 --- a/src/vs/base/test/node/extfs/extfs.test.ts +++ b/src/vs/base/test/node/extfs/extfs.test.ts @@ -17,7 +17,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; const ignore = () => { }; -const mkdirp = (path: string, mode: number, callback: (error) => void) => { +const mkdirp = (path: string, mode: number, callback: (error: any) => void) => { extfs.mkdirp(path, mode).then(() => callback(null), error => callback(error)); }; diff --git a/src/vs/base/test/node/glob.test.ts b/src/vs/base/test/node/glob.test.ts index af66975d9c..6d211bb8ac 100644 --- a/src/vs/base/test/node/glob.test.ts +++ b/src/vs/base/test/node/glob.test.ts @@ -431,7 +431,7 @@ suite('Glob', () => { test('expression support (single)', function () { let siblings = ['test.html', 'test.txt', 'test.ts', 'test.js']; - let hasSibling = name => siblings.indexOf(name) !== -1; + let hasSibling = (name: string) => siblings.indexOf(name) !== -1; // { "**/*.js": { "when": "$(basename).ts" } } let expression: glob.IExpression = { @@ -467,7 +467,7 @@ suite('Glob', () => { test('expression support (multiple)', function () { let siblings = ['test.html', 'test.txt', 'test.ts', 'test.js']; - let hasSibling = name => siblings.indexOf(name) !== -1; + let hasSibling = (name: string) => siblings.indexOf(name) !== -1; // { "**/*.js": { "when": "$(basename).ts" } } let expression: glob.IExpression = { @@ -717,7 +717,7 @@ suite('Glob', () => { }; let siblings = ['foo.ts', 'foo.js', 'foo', 'bar']; - let hasSibling = name => siblings.indexOf(name) !== -1; + let hasSibling = (name: string) => siblings.indexOf(name) !== -1; assert.strictEqual(glob.match(expr, 'bar', hasSibling), '**/bar'); assert.strictEqual(glob.match(expr, 'foo', hasSibling), null); @@ -774,7 +774,7 @@ suite('Glob', () => { let expr = { '**/*.js': { when: '$(basename).ts' } }; let siblings = ['foo.ts', 'foo.js']; - let hasSibling = name => siblings.indexOf(name) !== -1; + let hasSibling = (name: string) => siblings.indexOf(name) !== -1; assert.strictEqual(glob.parse(expr)('bar/baz.js', 'baz.js', hasSibling), null); assert.strictEqual(glob.parse(expr)('bar/foo.js', 'foo.js', hasSibling), '**/*.js'); @@ -818,7 +818,7 @@ suite('Glob', () => { ]); const siblings = ['baz', 'baz.zip', 'nope']; - const hasSibling = name => siblings.indexOf(name) !== -1; + const hasSibling = (name: string) => siblings.indexOf(name) !== -1; testOptimizationForBasenames({ '**/foo/**': { when: '$(basename).zip' }, '**/bar/**': true @@ -924,7 +924,7 @@ suite('Glob', () => { ]); const siblings = ['baz', 'baz.zip', 'nope']; - let hasSibling = name => siblings.indexOf(name) !== -1; + let hasSibling = (name: string) => siblings.indexOf(name) !== -1; testOptimizationForPaths({ '**/foo/123/**': { when: '$(basename).zip' }, '**/bar/123/**': true diff --git a/src/vs/base/test/node/storage/storage.test.ts b/src/vs/base/test/node/storage/storage.test.ts index 5988c34178..2b0d6d5645 100644 --- a/src/vs/base/test/node/storage/storage.test.ts +++ b/src/vs/base/test/node/storage/storage.test.ts @@ -293,7 +293,7 @@ suite('SQLite Storage Library', () => { return set; } - async function testDBBasics(path, logError?: (error) => void) { + async function testDBBasics(path: string, logError?: (error: Error) => void) { let options!: ISQLiteStorageDatabaseOptions; if (logError) { options = { diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index d645e14ba7..21a3bb0548 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -39,6 +39,7 @@ import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil'; import { Button } from 'vs/base/browser/ui/button/button'; +import { withUndefinedAsNull } from 'vs/base/common/types'; const MAX_URL_LENGTH = platform.isWindows ? 2081 : 5400; @@ -211,7 +212,7 @@ export class IssueReporter extends Disposable { styleTag.innerHTML = content.join('\n'); document.head.appendChild(styleTag); - document.body.style.color = styles.color || null; + document.body.style.color = withUndefinedAsNull(styles.color); } private handleExtensionData(extensions: IssueReporterExtensionData[]) { diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts index d40780dcc3..3dd7526f97 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts @@ -99,7 +99,7 @@ function attachTo(item: ProcessItem) { ipcRenderer.send('vscode:workbenchCommand', { id: 'debug.startFromConfig', from: 'processExplorer', args: [config] }); } -function getProcessIdWithHighestProperty(processList, propertyName: string) { +function getProcessIdWithHighestProperty(processList: any[], propertyName: string) { let max = 0; let maxProcessId; processList.forEach(process => { @@ -112,7 +112,7 @@ function getProcessIdWithHighestProperty(processList, propertyName: string) { return maxProcessId; } -function updateProcessInfo(processList): void { +function updateProcessInfo(processList: any[]): void { const container = document.getElementById('process-list'); if (!container) { return; @@ -199,12 +199,12 @@ function applyZoom(zoomLevel: number): void { browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false); } -function showContextMenu(e) { +function showContextMenu(e: MouseEvent) { e.preventDefault(); const items: IContextMenuItem[] = []; - const pid = parseInt(e.currentTarget.id); + const pid = parseInt((e.currentTarget as HTMLElement).id); if (pid && typeof pid === 'number') { items.push({ label: localize('killProcess', "Kill Process"), @@ -277,7 +277,7 @@ export function startup(data: ProcessExplorerData): void { applyZoom(data.zoomLevel); // Map window process pids to titles, annotate process names with this when rendering to distinguish between them - ipcRenderer.on('vscode:windowsInfoResponse', (event, windows) => { + ipcRenderer.on('vscode:windowsInfoResponse', (_event: unknown, windows: any[]) => { mapPidToWindowTitle = new Map(); windows.forEach(window => mapPidToWindowTitle.set(window.pid, window.title)); }); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index fbb9092102..dfe7da8c46 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -388,7 +388,7 @@ export class CodeApplication extends Disposable { this.logService.info(`Tracing: waiting for windows to get ready...`); let recordingStopped = false; - const stopRecording = (timeout) => { + const stopRecording = (timeout: boolean) => { if (recordingStopped) { return; } @@ -574,16 +574,17 @@ export class CodeApplication extends Disposable { const hasCliArgs = hasArgs(args._); const hasFolderURIs = hasArgs(args['folder-uri']); const hasFileURIs = hasArgs(args['file-uri']); + const noRecentEntry = args['skip-add-to-recently-opened'] === true; if (args['new-window'] && !hasCliArgs && !hasFolderURIs && !hasFileURIs) { - return this.windowsMainService.open({ context, cli: args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths + return this.windowsMainService.open({ context, cli: args, forceNewWindow: true, forceEmpty: true, noRecentEntry, initialStartup: true }); // new window if "-n" was used without paths } if (macOpenFiles && macOpenFiles.length && !hasCliArgs && !hasFolderURIs && !hasFileURIs) { - return this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, urisToOpen: macOpenFiles.map(file => ({ uri: URI.file(file) })), initialStartup: true }); // mac: open-file event received on startup + return this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, urisToOpen: macOpenFiles.map(file => ({ uri: URI.file(file) })), noRecentEntry, initialStartup: true }); // mac: open-file event received on startup } - return this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']), diffMode: args.diff, initialStartup: true }); // default: read paths from cli + return this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']), diffMode: args.diff, noRecentEntry, initialStartup: true }); // default: read paths from cli } private afterWindowOpen(accessor: ServicesAccessor): void { diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 6e2d6ac066..94fee5ccc1 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -489,7 +489,7 @@ export class WindowsManager implements IWindowsMainService { // Remember in recent document list (unless this opens for extension development) // Also do not add paths when files are opened for diffing, only if opened individually - if (!usedWindows.some(w => w.isExtensionDevelopmentHost) && !openConfig.diffMode && !this.environmentService.skipAddToRecentlyOpened) { + if (!usedWindows.some(w => w.isExtensionDevelopmentHost) && !openConfig.diffMode && !openConfig.noRecentEntry) { const recents: IRecent[] = []; for (let pathToOpen of pathsToOpen) { if (pathToOpen.workspace) { @@ -746,7 +746,7 @@ export class WindowsManager implements IWindowsMainService { private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, fileInputs?: IFileInputs): ICodeWindow { window.focus(); // make sure window has focus - const params: { filesToOpen?, filesToCreate?, filesToDiff?, filesToWait?, termProgram?} = {}; + const params: { filesToOpen?: IPath[], filesToCreate?: IPath[], filesToDiff?: IPath[], filesToWait?: IPathsToWaitFor, termProgram?: string } = {}; if (fileInputs) { params.filesToOpen = fileInputs.filesToOpen; params.filesToCreate = fileInputs.filesToCreate; @@ -1216,7 +1216,7 @@ export class WindowsManager implements IWindowsMainService { } // Open it - this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: !cliArgs.length && !folderUris.length && !fileUris.length, userEnv: openConfig.userEnv }); + this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: !cliArgs.length && !folderUris.length && !fileUris.length, userEnv: openConfig.userEnv, noRecentEntry: true }); } private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow { @@ -1897,9 +1897,15 @@ class Dialogs { showMessageBox(options: Electron.MessageBoxOptions, window?: ICodeWindow): Promise { return this.getDialogQueue(window).queue(() => { return new Promise(resolve => { - dialog.showMessageBox(window ? window.win : undefined!, options, (response: number, checkboxChecked: boolean) => { + const callback = (response: number, checkboxChecked: boolean) => { resolve({ button: response, checkboxChecked }); - }); + }; + + if (window) { + dialog.showMessageBox(window.win, options, callback); + } else { + dialog.showMessageBox(options, callback); + } }); }); } @@ -1916,9 +1922,15 @@ class Dialogs { return this.getDialogQueue(window).queue(() => { return new Promise(resolve => { - dialog.showSaveDialog(window ? window.win : undefined!, options, path => { + const callback = (path: string) => { resolve(normalizePath(path)); - }); + }; + + if (window) { + dialog.showSaveDialog(window.win, options, callback); + } else { + dialog.showSaveDialog(options, callback); + } }); }); } @@ -1948,9 +1960,15 @@ class Dialogs { // Show dialog and wrap as promise validatePathPromise.then(() => { - dialog.showOpenDialog(window ? window.win : undefined!, options, paths => { + const callback = (paths: string[]) => { resolve(normalizePaths(paths)); - }); + }; + + if (window) { + dialog.showOpenDialog(window.win, options, callback); + } else { + dialog.showOpenDialog(options, callback); + } }); }); }); diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index e38e71c17f..23141923ad 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -20,6 +20,7 @@ import { IConstructorSignature1, ServicesAccessor } from 'vs/platform/instantiat import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { withNullAsUndefined } from 'vs/base/common/types'; export type ServicesAccessor = ServicesAccessor; export type IEditorContributionCtor = IConstructorSignature1; @@ -88,7 +89,7 @@ export abstract class Command { id: this.id, handler: (accessor, args) => this.runCommand(accessor, args), weight: this._kbOpts.weight, - when: kbWhen || null, + when: kbWhen, primary: this._kbOpts.primary, secondary: this._kbOpts.secondary, win: this._kbOpts.win, @@ -156,7 +157,7 @@ export abstract class EditorCommand extends Command { return editor.invokeWithinContext((editorAccessor) => { const kbService = editorAccessor.get(IContextKeyService); - if (!kbService.contextMatchesRules(this.precondition)) { + if (!kbService.contextMatchesRules(withNullAsUndefined(this.precondition))) { // precondition does not hold return; } diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index f8feb04fcb..15f524f373 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -272,7 +272,7 @@ class Widget { public setPosition(position: IPosition | null | undefined, range: IRange | null | undefined, preference: ContentWidgetPositionPreference[] | null | undefined): void { this._setPosition(position, range); - this._preference = preference || null; + this._preference = withUndefinedAsNull(preference); this._cachedDomNodeClientWidth = -1; this._cachedDomNodeClientHeight = -1; } diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 7f77744330..e354933ac4 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -49,6 +49,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { INotificationService } from 'vs/platform/notification/common/notification'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { withNullAsUndefined } from 'vs/base/common/types'; let EDITOR_ID = 0; @@ -308,7 +309,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE action.id, action.label, action.alias, - action.precondition, + withNullAsUndefined(action.precondition), (): Promise => { return this._instantiationService.invokeFunction((accessor) => { return Promise.resolve(action.runEditorCommand(accessor, this, null)); @@ -1644,6 +1645,8 @@ export class EditorModeContext extends Disposable { private readonly _hasRenameProvider: IContextKey; private readonly _hasDocumentFormattingProvider: IContextKey; private readonly _hasDocumentSelectionFormattingProvider: IContextKey; + private readonly _hasMultipleDocumentFormattingProvider: IContextKey; + private readonly _hasMultipleDocumentSelectionFormattingProvider: IContextKey; private readonly _hasSignatureHelpProvider: IContextKey; private readonly _isInWalkThrough: IContextKey; @@ -1667,9 +1670,11 @@ export class EditorModeContext extends Disposable { this._hasDocumentSymbolProvider = EditorContextKeys.hasDocumentSymbolProvider.bindTo(contextKeyService); this._hasReferenceProvider = EditorContextKeys.hasReferenceProvider.bindTo(contextKeyService); this._hasRenameProvider = EditorContextKeys.hasRenameProvider.bindTo(contextKeyService); + this._hasSignatureHelpProvider = EditorContextKeys.hasSignatureHelpProvider.bindTo(contextKeyService); this._hasDocumentFormattingProvider = EditorContextKeys.hasDocumentFormattingProvider.bindTo(contextKeyService); this._hasDocumentSelectionFormattingProvider = EditorContextKeys.hasDocumentSelectionFormattingProvider.bindTo(contextKeyService); - this._hasSignatureHelpProvider = EditorContextKeys.hasSignatureHelpProvider.bindTo(contextKeyService); + this._hasMultipleDocumentFormattingProvider = EditorContextKeys.hasMultipleDocumentFormattingProvider.bindTo(contextKeyService); + this._hasMultipleDocumentSelectionFormattingProvider = EditorContextKeys.hasMultipleDocumentSelectionFormattingProvider.bindTo(contextKeyService); this._isInWalkThrough = EditorContextKeys.isInEmbeddedEditor.bindTo(contextKeyService); const update = () => this._update(); @@ -1744,6 +1749,8 @@ export class EditorModeContext extends Disposable { this._hasSignatureHelpProvider.set(modes.SignatureHelpProviderRegistry.has(model)); this._hasDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.has(model) || modes.DocumentRangeFormattingEditProviderRegistry.has(model)); this._hasDocumentSelectionFormattingProvider.set(modes.DocumentRangeFormattingEditProviderRegistry.has(model)); + this._hasMultipleDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.all(model).length > 1 || modes.DocumentRangeFormattingEditProviderRegistry.all(model).length > 1); + this._hasMultipleDocumentSelectionFormattingProvider.set(modes.DocumentRangeFormattingEditProviderRegistry.all(model).length > 1); this._isInWalkThrough.set(model.uri.scheme === Schemas.walkThroughSnippet); } } diff --git a/src/vs/editor/common/editorAction.ts b/src/vs/editor/common/editorAction.ts index 5b1efc18d0..c3c5abca3d 100644 --- a/src/vs/editor/common/editorAction.ts +++ b/src/vs/editor/common/editorAction.ts @@ -12,7 +12,7 @@ export class InternalEditorAction implements IEditorAction { public readonly label: string; public readonly alias: string; - private readonly _precondition: ContextKeyExpr | null; + private readonly _precondition: ContextKeyExpr | undefined; private readonly _run: () => Promise; private readonly _contextKeyService: IContextKeyService; @@ -20,7 +20,7 @@ export class InternalEditorAction implements IEditorAction { id: string, label: string, alias: string, - precondition: ContextKeyExpr | null, + precondition: ContextKeyExpr | undefined, run: () => Promise, contextKeyService: IContextKeyService ) { diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts index 38e05a3b92..e88e14c78b 100644 --- a/src/vs/editor/common/editorContextKeys.ts +++ b/src/vs/editor/common/editorContextKeys.ts @@ -46,7 +46,12 @@ export namespace EditorContextKeys { export const hasDocumentSymbolProvider = new RawContextKey('editorHasDocumentSymbolProvider', false); export const hasReferenceProvider = new RawContextKey('editorHasReferenceProvider', false); export const hasRenameProvider = new RawContextKey('editorHasRenameProvider', false); + export const hasSignatureHelpProvider = new RawContextKey('editorHasSignatureHelpProvider', false); + + // -- mode context keys: formatting export const hasDocumentFormattingProvider = new RawContextKey('editorHasDocumentFormattingProvider', false); export const hasDocumentSelectionFormattingProvider = new RawContextKey('editorHasDocumentSelectionFormattingProvider', false); - export const hasSignatureHelpProvider = new RawContextKey('editorHasSignatureHelpProvider', false); + export const hasMultipleDocumentFormattingProvider = new RawContextKey('editorHasMultipleDocumentFormattingProvider', false); + export const hasMultipleDocumentSelectionFormattingProvider = new RawContextKey('editorHasMultipleDocumentSelectionFormattingProvider', false); + } diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 51b87cf9a3..4915e17222 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -32,6 +32,7 @@ import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; import { BracketsUtils, RichEditBracket, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; import { IStringStream, ITextSnapshot } from 'vs/platform/files/common/files'; import { ITheme, ThemeColor } from 'vs/platform/theme/common/themeService'; +import { withUndefinedAsNull } from 'vs/base/common/types'; const CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048; @@ -2877,8 +2878,8 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions { this.stickiness = options.stickiness || model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges; this.zIndex = options.zIndex || 0; this.className = options.className ? cleanClassName(options.className) : null; - this.hoverMessage = options.hoverMessage || null; - this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || null; + this.hoverMessage = withUndefinedAsNull(options.hoverMessage); + this.glyphMarginHoverMessage = withUndefinedAsNull(options.glyphMarginHoverMessage); this.isWholeLine = options.isWholeLine || false; this.showIfCollapsed = options.showIfCollapsed || false; this.collapseOnReplaceEdit = options.collapseOnReplaceEdit || false; diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 7e59046d6a..7b775c9f96 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -929,6 +929,8 @@ export interface DocumentFormattingEditProvider { */ readonly extensionId?: ExtensionIdentifier; + readonly displayName?: string; + /** * Provide formatting edits for a whole document. */ @@ -939,13 +941,13 @@ export interface DocumentFormattingEditProvider { * the formatting-feature. */ export interface DocumentRangeFormattingEditProvider { - - /** * @internal */ readonly extensionId?: ExtensionIdentifier; + readonly displayName?: string; + /** * Provide formatting edits for a range in a document. * @@ -1396,6 +1398,24 @@ export interface WorkspaceCommentProvider { onDidChangeCommentThreads(): Event; } +/** + * @internal + */ +export interface IWebviewOptions { + readonly enableScripts?: boolean; + readonly enableCommandUris?: boolean; + readonly localResourceRoots?: ReadonlyArray; + readonly portMapping?: ReadonlyArray<{ port: number, resolvedPort: number }>; +} + +/** + * @internal + */ +export interface IWebviewPanelOptions { + readonly enableFindWidget?: boolean; + readonly retainContextWhenHidden?: boolean; +} + export interface ICodeLensSymbol { range: IRange; id?: string; diff --git a/src/vs/editor/common/services/editorWorkerService.ts b/src/vs/editor/common/services/editorWorkerService.ts index 25c57cc433..6d5a25341f 100644 --- a/src/vs/editor/common/services/editorWorkerService.ts +++ b/src/vs/editor/common/services/editorWorkerService.ts @@ -26,7 +26,7 @@ export interface IEditorWorkerService { canComputeDirtyDiff(original: URI, modified: URI): boolean; computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise; - computeMoreMinimalEdits(resource: URI, edits: TextEdit[] | null | undefined): Promise; + computeMoreMinimalEdits(resource: URI, edits: TextEdit[] | null | undefined): Promise; canComputeWordRanges(resource: URI): boolean; computeWordRanges(resource: URI, range: IRange): Promise<{ [word: string]: IRange[] } | null>; diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index ec5bfbd3e1..e7ed3ce7b4 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -20,6 +20,7 @@ import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/s import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { regExpFlags } from 'vs/base/common/strings'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; /** * Stop syncing a model to the worker if it was not needed for 1 min. @@ -88,14 +89,15 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker return this._workerManager.withWorker().then(client => client.computeDirtyDiff(original, modified, ignoreTrimWhitespace)); } - public computeMoreMinimalEdits(resource: URI, edits: modes.TextEdit[] | null | undefined): Promise { - if (!Array.isArray(edits) || edits.length === 0) { - return Promise.resolve(edits); - } else { + public computeMoreMinimalEdits(resource: URI, edits: modes.TextEdit[] | null | undefined): Promise { + if (isNonEmptyArray(edits)) { if (!canSyncModel(this._modelService, resource)) { return Promise.resolve(edits); // File too large } return this._workerManager.withWorker().then(client => client.computeMoreMinimalEdits(resource, edits)); + + } else { + return Promise.resolve(undefined); } } diff --git a/src/vs/editor/common/services/languagesRegistry.ts b/src/vs/editor/common/services/languagesRegistry.ts index f2d27baef5..551edafe41 100644 --- a/src/vs/editor/common/services/languagesRegistry.ts +++ b/src/vs/editor/common/services/languagesRegistry.ts @@ -15,6 +15,7 @@ import { NULL_LANGUAGE_IDENTIFIER, NULL_MODE_ID } from 'vs/editor/common/modes/n import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; +import { withUndefinedAsNull } from 'vs/base/common/types'; const hasOwnProperty = Object.prototype.hasOwnProperty; @@ -267,7 +268,7 @@ export class LanguagesRegistry extends Disposable { return null; } const language = this._languages[modeId]; - return (language.mimetypes[0] || null); + return withUndefinedAsNull(language.mimetypes[0]); } public extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds: string | undefined): string[] { diff --git a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts index e4e3108509..07978df937 100644 --- a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts +++ b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts @@ -16,6 +16,7 @@ import { keys } from 'vs/base/common/map'; import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; import { Schemas } from 'vs/base/common/network'; import { Emitter, Event } from 'vs/base/common/event'; +import { withUndefinedAsNull } from 'vs/base/common/types'; function MODEL_ID(resource: URI): string { return resource.toString(); @@ -80,7 +81,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor getMarker(model: ITextModel, decoration: IModelDecoration): IMarker | null { const markerDecorations = this._markerDecorations.get(MODEL_ID(model.uri)); - return markerDecorations ? markerDecorations.getMarker(decoration) || null : null; + return markerDecorations ? withUndefinedAsNull(markerDecorations.getMarker(decoration)) : null; } getLiveMarkers(model: ITextModel): [Range, IMarker][] { diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 2cbf5f9354..41d45f8bb1 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -163,7 +163,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget { const lineContent = model.getLineContent(lineNumber); const indent = TextModel.computeIndentLevel(lineContent, tabSize); const lineHasSpace = config.fontInfo.spaceWidth * indent > 22; - const isFolded = (lineNumber) => { + const isFolded = (lineNumber: number) => { return lineNumber > 2 && this._editor.getTopForLineNumber(lineNumber) === this._editor.getTopForLineNumber(lineNumber - 1); }; diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index 584ae2c8e8..fe38ca4d63 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -25,7 +25,7 @@ class CodeLensViewZone implements editorBrowser.IViewZone { afterLineNumber: number; - private _lastHeight: number; + private _lastHeight?: number; private readonly _onHeight: Function; constructor(afterLineNumber: number, onHeight: Function) { diff --git a/src/vs/editor/contrib/find/test/findController.test.ts b/src/vs/editor/contrib/find/test/findController.test.ts index 691062b24a..87a07bea5c 100644 --- a/src/vs/editor/contrib/find/test/findController.test.ts +++ b/src/vs/editor/contrib/find/test/findController.test.ts @@ -67,7 +67,7 @@ suite('FindController', () => { getBoolean: (key: string) => !!queryState[key], getNumber: (key: string) => undefined, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, - remove: (key) => undefined + remove: () => undefined } as any); if (platform.isMacintosh) { @@ -442,7 +442,7 @@ suite('FindController query options persistence', () => { getBoolean: (key: string) => !!queryState[key], getNumber: (key: string) => undefined, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, - remove: (key) => undefined + remove: () => undefined } as any); test('matchCase', () => { diff --git a/src/vs/editor/contrib/folding/foldingModel.ts b/src/vs/editor/contrib/folding/foldingModel.ts index d7c92ee77f..024b311b91 100644 --- a/src/vs/editor/contrib/folding/foldingModel.ts +++ b/src/vs/editor/contrib/folding/foldingModel.ts @@ -66,7 +66,7 @@ export class FoldingModel { public update(newRegions: FoldingRegions, blockedLineNumers: number[] = []): void { let newEditorDecorations: IModelDeltaDecoration[] = []; - let isBlocked = (startLineNumber, endLineNumber) => { + let isBlocked = (startLineNumber: number, endLineNumber: number) => { for (let blockedLineNumber of blockedLineNumers) { if (startLineNumber < blockedLineNumber && blockedLineNumber <= endLineNumber) { // first line is visible return true; diff --git a/src/vs/editor/contrib/format/format.ts b/src/vs/editor/contrib/format/format.ts index 74bf8ab378..06f0b0fb97 100644 --- a/src/vs/editor/contrib/format/format.ts +++ b/src/vs/editor/contrib/format/format.ts @@ -3,138 +3,258 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { Range } from 'vs/editor/common/core/range'; -import { ITextModel } from 'vs/editor/common/model'; -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; -import { DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry, FormattingOptions, TextEdit } from 'vs/editor/common/modes'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { first } from 'vs/base/common/async'; +import { CodeEditorStateFlag, EditorState } from 'vs/editor/browser/core/editorState'; +import { IActiveCodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { registerLanguageCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ISingleEditOperation, ITextModel } from 'vs/editor/common/model'; +import { DocumentFormattingEditProvider, DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProvider, DocumentRangeFormattingEditProviderRegistry, FormattingOptions, OnTypeFormattingEditProviderRegistry, TextEdit } from 'vs/editor/common/modes'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit'; +import * as nls from 'vs/nls'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IDisposable } from 'vs/base/common/lifecycle'; -export const enum FormatMode { - Auto = 1, - Manual = 2, -} +export function alertFormattingEdits(edits: ISingleEditOperation[]): void { -export const enum FormatKind { - Document = 8, - Range = 16, - OnType = 32, -} + edits = edits.filter(edit => edit.range); + if (!edits.length) { + return; + } -export interface IFormatterConflictCallback { - (extensionIds: (ExtensionIdentifier | undefined)[], model: ITextModel, mode: number): void; -} - -let _conflictResolver: IFormatterConflictCallback | undefined; - -export function setFormatterConflictCallback(callback: IFormatterConflictCallback): IDisposable { - let oldCallback = _conflictResolver; - _conflictResolver = callback; - return { - dispose() { - if (oldCallback) { - _conflictResolver = oldCallback; - oldCallback = undefined; - } + let { range } = edits[0]; + for (let i = 1; i < edits.length; i++) { + range = Range.plusRange(range, edits[i].range); + } + const { startLineNumber, endLineNumber } = range; + if (startLineNumber === endLineNumber) { + if (edits.length === 1) { + alert(nls.localize('hint11', "Made 1 formatting edit on line {0}", startLineNumber)); + } else { + alert(nls.localize('hintn1', "Made {0} formatting edits on line {1}", edits.length, startLineNumber)); + } + } else { + if (edits.length === 1) { + alert(nls.localize('hint1n', "Made 1 formatting edit between lines {0} and {1}", startLineNumber, endLineNumber)); + } else { + alert(nls.localize('hintnn', "Made {0} formatting edits between lines {1} and {2}", edits.length, startLineNumber, endLineNumber)); } - }; -} - -function invokeFormatterCallback(formatter: T[], model: ITextModel, mode: number): void { - if (_conflictResolver) { - const ids = formatter.map(formatter => formatter.extensionId); - _conflictResolver(ids, model, mode); } } -export async function getDocumentRangeFormattingEdits( - telemetryService: ITelemetryService, +export function getRealAndSyntheticDocumentFormattersOrdered(model: ITextModel): DocumentFormattingEditProvider[] { + const result: DocumentFormattingEditProvider[] = []; + const seen = new Set(); + + // (1) add all document formatter + const docFormatter = DocumentFormattingEditProviderRegistry.ordered(model); + for (const formatter of docFormatter) { + result.push(formatter); + if (formatter.extensionId) { + seen.add(ExtensionIdentifier.toKey(formatter.extensionId)); + } + } + + // (2) add all range formatter as document formatter (unless the same extension already did that) + const rangeFormatter = DocumentRangeFormattingEditProviderRegistry.ordered(model); + for (const formatter of rangeFormatter) { + if (formatter.extensionId) { + if (seen.has(ExtensionIdentifier.toKey(formatter.extensionId))) { + continue; + } + seen.add(ExtensionIdentifier.toKey(formatter.extensionId)); + } + result.push({ + displayName: formatter.displayName, + extensionId: formatter.extensionId, + provideDocumentFormattingEdits(model, options, token) { + return formatter.provideDocumentRangeFormattingEdits(model, model.getFullModelRange(), options, token); + } + }); + } + return result; +} + +export async function formatDocumentRangeWithProvider( + accessor: ServicesAccessor, + provider: DocumentRangeFormattingEditProvider, + editorOrModel: ITextModel | IActiveCodeEditor, + range: Range, + token: CancellationToken +): Promise { + const workerService = accessor.get(IEditorWorkerService); + + let model: ITextModel; + let validate: () => boolean; + if (isCodeEditor(editorOrModel)) { + model = editorOrModel.getModel(); + const state = new EditorState(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); + validate = () => state.validate(editorOrModel); + } else { + model = editorOrModel; + const versionNow = editorOrModel.getVersionId(); + validate = () => versionNow === editorOrModel.getVersionId(); + } + + const rawEdits = await provider.provideDocumentRangeFormattingEdits( + model, + range, + model.getFormattingOptions(), + token + ); + + const edits = await workerService.computeMoreMinimalEdits(model.uri, rawEdits); + + if (!validate()) { + return true; + } + + if (!edits || edits.length === 0) { + return false; + } + + if (isCodeEditor(editorOrModel)) { + // use editor to apply edits + FormattingEdit.execute(editorOrModel, edits); + alertFormattingEdits(edits); + editorOrModel.pushUndoStop(); + editorOrModel.focus(); + editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), editorCommon.ScrollType.Immediate); + + } else { + // use model to apply edits + const [{ range }] = edits; + const initialSelection = new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); + model.pushEditOperations([initialSelection], edits.map(edit => { + return { + text: edit.text, + range: Range.lift(edit.range), + forceMoveMarkers: true + }; + }), undoEdits => { + for (const { range } of undoEdits) { + if (Range.areIntersectingOrTouching(range, initialSelection)) { + return [new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)]; + } + } + return null; + }); + } + + return true; +} + +export async function formatDocumentWithProvider( + accessor: ServicesAccessor, + provider: DocumentFormattingEditProvider, + editorOrModel: ITextModel | IActiveCodeEditor, + token: CancellationToken +): Promise { + const workerService = accessor.get(IEditorWorkerService); + + let model: ITextModel; + let validate: () => boolean; + if (isCodeEditor(editorOrModel)) { + model = editorOrModel.getModel(); + const state = new EditorState(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); + validate = () => state.validate(editorOrModel); + } else { + model = editorOrModel; + const versionNow = editorOrModel.getVersionId(); + validate = () => versionNow === editorOrModel.getVersionId(); + } + + const rawEdits = await provider.provideDocumentFormattingEdits( + model, + model.getFormattingOptions(), + token + ); + + const edits = await workerService.computeMoreMinimalEdits(model.uri, rawEdits); + + if (!validate()) { + return true; + } + + if (!edits || edits.length === 0) { + return false; + } + + if (isCodeEditor(editorOrModel)) { + // use editor to apply edits + FormattingEdit.execute(editorOrModel, edits); + alertFormattingEdits(edits); + editorOrModel.pushUndoStop(); + editorOrModel.focus(); + editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), editorCommon.ScrollType.Immediate); + + } else { + // use model to apply edits + const [{ range }] = edits; + const initialSelection = new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); + model.pushEditOperations([initialSelection], edits.map(edit => { + return { + text: edit.text, + range: Range.lift(edit.range), + forceMoveMarkers: true + }; + }), undoEdits => { + for (const { range } of undoEdits) { + if (Range.areIntersectingOrTouching(range, initialSelection)) { + return [new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)]; + } + } + return null; + }); + } + + return true; +} + +export async function getDocumentRangeFormattingEditsUntilResult( workerService: IEditorWorkerService, model: ITextModel, range: Range, options: FormattingOptions, - mode: FormatMode, token: CancellationToken -): Promise { +): Promise { const providers = DocumentRangeFormattingEditProviderRegistry.ordered(model); - - /* __GDPR__ - "formatterInfo" : { - "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "extensions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + for (const provider of providers) { + let rawEdits = await Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)).catch(onUnexpectedExternalError); + if (isNonEmptyArray(rawEdits)) { + return await workerService.computeMoreMinimalEdits(model.uri, rawEdits); } - */ - telemetryService.publicLog('formatterInfo', { - type: 'range', - language: model.getLanguageIdentifier().language, - count: providers.length, - extensions: providers.map(p => p.extensionId ? ExtensionIdentifier.toKey(p.extensionId) : 'unknown') - }); - - invokeFormatterCallback(providers, model, mode | FormatKind.Range); - - return first(providers.map(provider => () => { - return Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)).catch(onUnexpectedExternalError); - }), isNonEmptyArray).then(edits => { - // break edits into smaller edits - return workerService.computeMoreMinimalEdits(model.uri, edits); - }); + } + return undefined; } -export function getDocumentFormattingEdits( - telemetryService: ITelemetryService, +export async function getDocumentFormattingEditsUntilResult( workerService: IEditorWorkerService, model: ITextModel, options: FormattingOptions, - mode: FormatMode, token: CancellationToken -): Promise { +): Promise { - const docFormattingProviders = DocumentFormattingEditProviderRegistry.ordered(model); - - /* __GDPR__ - "formatterInfo" : { - "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "extensions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + const providers = getRealAndSyntheticDocumentFormattersOrdered(model); + for (const provider of providers) { + let rawEdits = await Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)).catch(onUnexpectedExternalError); + if (isNonEmptyArray(rawEdits)) { + return await workerService.computeMoreMinimalEdits(model.uri, rawEdits); } - */ - telemetryService.publicLog('formatterInfo', { - type: 'document', - language: model.getLanguageIdentifier().language, - count: docFormattingProviders.length, - extensions: docFormattingProviders.map(p => p.extensionId ? ExtensionIdentifier.toKey(p.extensionId) : 'unknown') - }); - - if (docFormattingProviders.length > 0) { - return first(docFormattingProviders.map(provider => () => { - // first with result wins... - return Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)).catch(onUnexpectedExternalError); - }), isNonEmptyArray).then(edits => { - // break edits into smaller edits - return workerService.computeMoreMinimalEdits(model.uri, edits); - }); - } else { - // try range formatters when no document formatter is registered - return getDocumentRangeFormattingEdits(telemetryService, workerService, model, model.getFullModelRange(), options, mode | FormatKind.Document, token); } + return undefined; } export function getOnTypeFormattingEdits( - telemetryService: ITelemetryService, workerService: IEditorWorkerService, model: ITextModel, position: Position, @@ -144,21 +264,6 @@ export function getOnTypeFormattingEdits( const providers = OnTypeFormattingEditProviderRegistry.ordered(model); - /* __GDPR__ - "formatterInfo" : { - "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "extensions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - telemetryService.publicLog('formatterInfo', { - type: 'ontype', - language: model.getLanguageIdentifier().language, - count: providers.length, - extensions: providers.map(p => p.extensionId ? ExtensionIdentifier.toKey(p.extensionId) : 'unknown') - }); - if (providers.length === 0) { return Promise.resolve(undefined); } @@ -181,7 +286,7 @@ registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) if (!model) { throw illegalArgument('resource'); } - return getDocumentRangeFormattingEdits(accessor.get(ITelemetryService), accessor.get(IEditorWorkerService), model, Range.lift(range), options, FormatMode.Auto, CancellationToken.None); + return getDocumentRangeFormattingEditsUntilResult(accessor.get(IEditorWorkerService), model, Range.lift(range), options, CancellationToken.None); }); registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, args) { @@ -194,7 +299,7 @@ registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, ar throw illegalArgument('resource'); } - return getDocumentFormattingEdits(accessor.get(ITelemetryService), accessor.get(IEditorWorkerService), model, options, FormatMode.Auto, CancellationToken.None); + return getDocumentFormattingEditsUntilResult(accessor.get(IEditorWorkerService), model, options, CancellationToken.None); }); registerLanguageCommand('_executeFormatOnTypeProvider', function (accessor, args) { @@ -207,5 +312,5 @@ registerLanguageCommand('_executeFormatOnTypeProvider', function (accessor, args throw illegalArgument('resource'); } - return getOnTypeFormattingEdits(accessor.get(ITelemetryService), accessor.get(IEditorWorkerService), model, Position.lift(position), ch, options); + return getOnTypeFormattingEdits(accessor.get(IEditorWorkerService), model, Position.lift(position), ch, options); }); diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 51255e9f36..2ddb4f6f41 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -3,122 +3,27 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { alert } from 'vs/base/browser/ui/aria/aria'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { CodeEditorStateFlag, EditorState } from 'vs/editor/browser/core/editorState'; -import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, registerEditorAction, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { ISingleEditOperation } from 'vs/editor/common/model'; -import { DocumentRangeFormattingEditProviderRegistry, FormattingOptions, OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes'; +import { DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; -import { getOnTypeFormattingEdits, getDocumentFormattingEdits, getDocumentRangeFormattingEdits, FormatMode } from 'vs/editor/contrib/format/format'; +import { getOnTypeFormattingEdits, formatDocumentWithProvider, formatDocumentRangeWithProvider, alertFormattingEdits, getRealAndSyntheticDocumentFormattersOrdered } from 'vs/editor/contrib/format/format'; import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit'; import * as nls from 'vs/nls'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; - -function alertFormattingEdits(edits: ISingleEditOperation[]): void { - - edits = edits.filter(edit => edit.range); - if (!edits.length) { - return; - } - - let { range } = edits[0]; - for (let i = 1; i < edits.length; i++) { - range = Range.plusRange(range, edits[i].range); - } - const { startLineNumber, endLineNumber } = range; - if (startLineNumber === endLineNumber) { - if (edits.length === 1) { - alert(nls.localize('hint11', "Made 1 formatting edit on line {0}", startLineNumber)); - } else { - alert(nls.localize('hintn1', "Made {0} formatting edits on line {1}", edits.length, startLineNumber)); - } - } else { - if (edits.length === 1) { - alert(nls.localize('hint1n', "Made 1 formatting edit between lines {0} and {1}", startLineNumber, endLineNumber)); - } else { - alert(nls.localize('hintnn', "Made {0} formatting edits between lines {1} and {2}", edits.length, startLineNumber, endLineNumber)); - } - } -} - -const enum FormatRangeType { - Full, - Selection, -} - -function formatDocumentRange( - telemetryService: ITelemetryService, - workerService: IEditorWorkerService, - editor: IActiveCodeEditor, - rangeOrRangeType: Range | FormatRangeType, - options: FormattingOptions, - token: CancellationToken -): Promise { - - - const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); - const model = editor.getModel(); - - let range: Range; - if (rangeOrRangeType === FormatRangeType.Full) { - // full - range = model.getFullModelRange(); - - } else if (rangeOrRangeType === FormatRangeType.Selection) { - // selection or line (when empty) - range = editor.getSelection(); - if (range.isEmpty()) { - range = new Range(range.startLineNumber, 1, range.endLineNumber, model.getLineMaxColumn(range.endLineNumber)); - } - } else { - // as is - range = rangeOrRangeType; - } - - return getDocumentRangeFormattingEdits(telemetryService, workerService, model, range, options, FormatMode.Manual, token).then(edits => { - // make edit only when the editor didn't change while - // computing and only when there are edits - if (state.validate(editor) && isNonEmptyArray(edits)) { - FormattingEdit.execute(editor, edits); - alertFormattingEdits(edits); - editor.focus(); - editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate); - } - }); - -} - -function formatDocument(telemetryService: ITelemetryService, workerService: IEditorWorkerService, editor: IActiveCodeEditor, options: FormattingOptions, token: CancellationToken): Promise { - - const allEdits: ISingleEditOperation[] = []; - const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); - - return getDocumentFormattingEdits(telemetryService, workerService, editor.getModel(), options, FormatMode.Manual, token).then(edits => { - // make edit only when the editor didn't change while - // computing and only when there are edits - if (state.validate(editor) && isNonEmptyArray(edits)) { - FormattingEdit.execute(editor, edits); - - alertFormattingEdits(allEdits); - editor.pushUndoStop(); - editor.focus(); - editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate); - } - }); -} +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { onUnexpectedError } from 'vs/base/common/errors'; class FormatOnType implements editorCommon.IEditorContribution { @@ -130,17 +35,25 @@ class FormatOnType implements editorCommon.IEditorContribution { constructor( editor: ICodeEditor, - @ITelemetryService private readonly _telemetryService: ITelemetryService, @IEditorWorkerService private readonly _workerService: IEditorWorkerService ) { this._editor = editor; - this._callOnDispose.push(editor.onDidChangeConfiguration(() => this.update())); - this._callOnDispose.push(editor.onDidChangeModel(() => this.update())); - this._callOnDispose.push(editor.onDidChangeModelLanguage(() => this.update())); - this._callOnDispose.push(OnTypeFormattingEditProviderRegistry.onDidChange(this.update, this)); + this._callOnDispose.push(editor.onDidChangeConfiguration(() => this._update())); + this._callOnDispose.push(editor.onDidChangeModel(() => this._update())); + this._callOnDispose.push(editor.onDidChangeModelLanguage(() => this._update())); + this._callOnDispose.push(OnTypeFormattingEditProviderRegistry.onDidChange(this._update, this)); } - private update(): void { + getId(): string { + return FormatOnType.ID; + } + + dispose(): void { + this._callOnDispose = dispose(this._callOnDispose); + this._callOnModel = dispose(this._callOnModel); + } + + private _update(): void { // clean up this._callOnModel = dispose(this._callOnModel); @@ -171,12 +84,12 @@ class FormatOnType implements editorCommon.IEditorContribution { this._callOnModel.push(this._editor.onDidType((text: string) => { let lastCharCode = text.charCodeAt(text.length - 1); if (triggerChars.has(lastCharCode)) { - this.trigger(String.fromCharCode(lastCharCode)); + this._trigger(String.fromCharCode(lastCharCode)); } })); } - private trigger(ch: string): void { + private _trigger(ch: string): void { if (!this._editor.hasModel()) { return; } @@ -214,7 +127,6 @@ class FormatOnType implements editorCommon.IEditorContribution { }); getOnTypeFormattingEdits( - this._telemetryService, this._workerService, model, position, @@ -238,42 +150,41 @@ class FormatOnType implements editorCommon.IEditorContribution { throw err; }); } - - public getId(): string { - return FormatOnType.ID; - } - - public dispose(): void { - this._callOnDispose = dispose(this._callOnDispose); - this._callOnModel = dispose(this._callOnModel); - } } class FormatOnPaste implements editorCommon.IEditorContribution { private static readonly ID = 'editor.contrib.formatOnPaste'; - private callOnDispose: IDisposable[]; - private callOnModel: IDisposable[]; + private _callOnDispose: IDisposable[]; + private _callOnModel: IDisposable[]; constructor( private readonly editor: ICodeEditor, - @IEditorWorkerService private readonly workerService: IEditorWorkerService, - @ITelemetryService private readonly telemetryService: ITelemetryService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { - this.callOnDispose = []; - this.callOnModel = []; + this._callOnDispose = []; + this._callOnModel = []; - this.callOnDispose.push(editor.onDidChangeConfiguration(() => this.update())); - this.callOnDispose.push(editor.onDidChangeModel(() => this.update())); - this.callOnDispose.push(editor.onDidChangeModelLanguage(() => this.update())); - this.callOnDispose.push(DocumentRangeFormattingEditProviderRegistry.onDidChange(this.update, this)); + this._callOnDispose.push(editor.onDidChangeConfiguration(() => this._update())); + this._callOnDispose.push(editor.onDidChangeModel(() => this._update())); + this._callOnDispose.push(editor.onDidChangeModelLanguage(() => this._update())); + this._callOnDispose.push(DocumentRangeFormattingEditProviderRegistry.onDidChange(this._update, this)); } - private update(): void { + getId(): string { + return FormatOnPaste.ID; + } + + dispose(): void { + this._callOnDispose = dispose(this._callOnDispose); + this._callOnModel = dispose(this._callOnModel); + } + + private _update(): void { // clean up - this.callOnModel = dispose(this.callOnModel); + this._callOnModel = dispose(this._callOnModel); // we are disabled if (!this.editor.getConfiguration().contribInfo.formatOnPaste) { @@ -285,53 +196,41 @@ class FormatOnPaste implements editorCommon.IEditorContribution { return; } - let model = this.editor.getModel(); - - // no support - if (!DocumentRangeFormattingEditProviderRegistry.has(model)) { + // no formatter + if (!DocumentRangeFormattingEditProviderRegistry.has(this.editor.getModel())) { return; } - this.callOnModel.push(this.editor.onDidPaste((range: Range) => { - this.trigger(range); - })); + this._callOnModel.push(this.editor.onDidPaste(range => this._trigger(range))); } - private trigger(range: Range): void { + private _trigger(range: Range): void { if (!this.editor.hasModel()) { return; } - if (this.editor.getSelections().length > 1) { return; } - - const model = this.editor.getModel(); - formatDocumentRange(this.telemetryService, this.workerService, this.editor, range, model.getFormattingOptions(), CancellationToken.None); - } - - public getId(): string { - return FormatOnPaste.ID; - } - - public dispose(): void { - this.callOnDispose = dispose(this.callOnDispose); - this.callOnModel = dispose(this.callOnModel); + const provider = DocumentRangeFormattingEditProviderRegistry.ordered(this.editor.getModel()); + if (provider.length !== 1) { + // print status in n>1 case? + return; + } + this._instantiationService.invokeFunction(formatDocumentRangeWithProvider, provider[0], this.editor, range, CancellationToken.None).catch(onUnexpectedError); } } -export class FormatDocumentAction extends EditorAction { +class FormatDocumentAction extends EditorAction { constructor() { super({ id: 'editor.action.formatDocument', label: nls.localize('formatDocument.label', "Format Document"), alias: 'Format Document', - precondition: EditorContextKeys.writable, + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider, EditorContextKeys.hasMultipleDocumentFormattingProvider.toNegated()), kbOpts: { - kbExpr: EditorContextKeys.editorTextFocus, + kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentFormattingProvider), primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, - // secondary: [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D)], linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }, weight: KeybindingWeight.EditorContrib }, @@ -343,26 +242,29 @@ export class FormatDocumentAction extends EditorAction { }); } - run(accessor: ServicesAccessor, editor: ICodeEditor): Promise | void { + async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { if (!editor.hasModel()) { return; } - const workerService = accessor.get(IEditorWorkerService); - const telemetryService = accessor.get(ITelemetryService); - return formatDocument(telemetryService, workerService, editor, editor.getModel().getFormattingOptions(), CancellationToken.None); + const instaService = accessor.get(IInstantiationService); + const model = editor.getModel(); + const [provider] = getRealAndSyntheticDocumentFormattersOrdered(model); + if (provider) { + await instaService.invokeFunction(formatDocumentWithProvider, provider, editor, CancellationToken.None); + } } } -export class FormatSelectionAction extends EditorAction { +class FormatSelectionAction extends EditorAction { constructor() { super({ id: 'editor.action.formatSelection', label: nls.localize('formatSelection.label', "Format Selection"), alias: 'Format Code', - precondition: ContextKeyExpr.and(EditorContextKeys.writable), + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentSelectionFormattingProvider, EditorContextKeys.hasMultipleDocumentSelectionFormattingProvider.toNegated()), kbOpts: { - kbExpr: EditorContextKeys.editorTextFocus, + kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentSelectionFormattingProvider), primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F), weight: KeybindingWeight.EditorContrib }, @@ -374,13 +276,19 @@ export class FormatSelectionAction extends EditorAction { }); } - run(accessor: ServicesAccessor, editor: ICodeEditor): Promise | void { + async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { if (!editor.hasModel()) { return; } - const workerService = accessor.get(IEditorWorkerService); - const telemetryService = accessor.get(ITelemetryService); - return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, editor.getModel().getFormattingOptions(), CancellationToken.None); + const instaService = accessor.get(IInstantiationService); + const [best] = DocumentRangeFormattingEditProviderRegistry.ordered(editor.getModel()); + if (best) { + let range: Range = editor.getSelection(); + if (range.isEmpty()) { + range = new Range(range.startLineNumber, 1, range.startLineNumber, editor.getModel().getLineMaxColumn(range.startLineNumber)); + } + await instaService.invokeFunction(formatDocumentRangeWithProvider, best, editor, range, CancellationToken.None); + } } } @@ -391,17 +299,15 @@ registerEditorAction(FormatSelectionAction); // this is the old format action that does both (format document OR format selection) // and we keep it here such that existing keybinding configurations etc will still work -CommandsRegistry.registerCommand('editor.action.format', accessor => { +CommandsRegistry.registerCommand('editor.action.format', async accessor => { const editor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); if (!editor || !editor.hasModel()) { - return undefined; + return; } - const workerService = accessor.get(IEditorWorkerService); - const telemetryService = accessor.get(ITelemetryService); - + const commandService = accessor.get(ICommandService); if (editor.getSelection().isEmpty()) { - return formatDocument(telemetryService, workerService, editor, editor.getModel().getFormattingOptions(), CancellationToken.None); + await commandService.executeCommand('editor.action.formatDocument'); } else { - return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, editor.getModel().getFormattingOptions(), CancellationToken.None); + await commandService.executeCommand('editor.action.formatSelection'); } }); diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 6eeae6aee1..23a3f009c1 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -203,8 +203,8 @@ export class MarkerController implements editorCommon.IEditorContribution { } private readonly _editor: ICodeEditor; - private _model: MarkerModel | null; - private _widget: MarkerNavigationWidget | null; + private _model: MarkerModel | null = null; + private _widget: MarkerNavigationWidget | null = null; private readonly _widgetVisible: IContextKey; private _disposeOnClose: IDisposable[] = []; diff --git a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts index 98dd43a2f2..8c2afca2f5 100644 --- a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts @@ -21,7 +21,7 @@ export class MoveLinesCommand implements ICommand { private readonly _autoIndent: boolean; private _selectionId: string; - private _moveEndPositionDown: boolean; + private _moveEndPositionDown?: boolean; private _moveEndLineSelectionShrink: boolean; constructor(selection: Selection, isMovingDown: boolean, autoIndent: boolean) { diff --git a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts index b04822e3e3..130c20779e 100644 --- a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts @@ -196,10 +196,9 @@ suite('Editor Contrib - Line Operations', () => { const endOfNonono = new Selection(5, 11, 5, 11); editor.setSelections([beforeSecondWasoSelection, endOfBCCSelection, endOfNonono]); - let selections; deleteAllLeftAction.run(null!, editor); - selections = editor.getSelections(); + let selections = editor.getSelections()!; assert.equal(model.getLineContent(2), ''); assert.equal(model.getLineContent(3), ' waso waso'); @@ -227,7 +226,7 @@ suite('Editor Contrib - Line Operations', () => { ], [5, 1, 5, 1]); deleteAllLeftAction.run(null!, editor); - selections = editor.getSelections(); + selections = editor.getSelections()!; assert.equal(model.getLineContent(1), 'hi my name is Carlos Matos waso waso'); assert.equal(selections.length, 2); diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index d9797ef0a2..d195664be9 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -21,6 +21,7 @@ import { Choice, Placeholder, SnippetParser, Text, TextmateSnippet } from './sni import { ClipboardBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, SelectionBasedVariableResolver, TimeBasedVariableResolver, CommentBasedVariableResolver, WorkspaceBasedVariableResolver } from './snippetVariables'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import * as colors from 'vs/platform/theme/common/colorRegistry'; +import { withNullAsUndefined } from 'vs/base/common/types'; registerThemingParticipant((theme, collector) => { @@ -281,7 +282,7 @@ export class OneSnippet { let result: Range | undefined; const model = this._editor.getModel(); this._placeholderDecorations.forEach((decorationId) => { - const placeholderRange = model.getDecorationRange(decorationId) || undefined; + const placeholderRange = withNullAsUndefined(model.getDecorationRange(decorationId)); if (!result) { result = placeholderRange; } else { diff --git a/src/vs/editor/contrib/suggest/suggestAlternatives.ts b/src/vs/editor/contrib/suggest/suggestAlternatives.ts index e48a3702fd..1df6a49b85 100644 --- a/src/vs/editor/contrib/suggest/suggestAlternatives.ts +++ b/src/vs/editor/contrib/suggest/suggestAlternatives.ts @@ -18,8 +18,8 @@ export class SuggestAlternatives { private _index: number; private _model: CompletionModel | undefined; private _acceptNext: ((selected: ISelectedSuggestion) => any) | undefined; - private _listener: IDisposable; - private _ignore: boolean; + private _listener: IDisposable | undefined; + private _ignore: boolean | undefined; constructor( private readonly _editor: ICodeEditor, diff --git a/src/vs/editor/standalone/browser/colorizer.ts b/src/vs/editor/standalone/browser/colorizer.ts index 500d535798..c84b65a42b 100644 --- a/src/vs/editor/standalone/browser/colorizer.ts +++ b/src/vs/editor/standalone/browser/colorizer.ts @@ -63,16 +63,16 @@ export class Colorizer { // Send out the event to create the mode modeService.triggerMode(language); - let tokenizationSupport = TokenizationRegistry.get(language); + const tokenizationSupport = TokenizationRegistry.get(language); if (tokenizationSupport) { return _colorize(lines, tabSize, tokenizationSupport); } - let tokenizationSupportPromise = TokenizationRegistry.getPromise(language); + const tokenizationSupportPromise = TokenizationRegistry.getPromise(language); if (tokenizationSupportPromise) { // A tokenizer will be registered soon return new Promise((resolve, reject) => { - tokenizationSupportPromise!.then(tokenizationSupport => { + tokenizationSupportPromise.then(tokenizationSupport => { _colorize(lines, tabSize, tokenizationSupport).then(resolve, reject); }, reject); }); diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index aba4a22666..d0c3c5fa7d 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -280,7 +280,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { })); } - public addDynamicKeybinding(commandId: string, _keybinding: number, handler: ICommandHandler, when: ContextKeyExpr | null): IDisposable { + public addDynamicKeybinding(commandId: string, _keybinding: number, handler: ICommandHandler, when: ContextKeyExpr | undefined): IDisposable { const keybinding = createKeybinding(_keybinding, OS); if (!keybinding) { throw new Error(`Invalid keybinding`); @@ -342,7 +342,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { private _toNormalizedKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { let result: ResolvedKeybindingItem[] = [], resultLen = 0; for (const item of items) { - const when = (item.when ? item.when.normalize() : null); + const when = (item.when ? item.when.normalize() : undefined); const keybinding = item.keybinding; if (!keybinding) { @@ -665,9 +665,5 @@ export class SimpleLayoutService implements ILayoutService { return this._container; } - get hasWorkbench(): boolean { - return false; - } - constructor(private _container: HTMLElement) { } } diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 90b2baf3ac..1846a058de 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -202,7 +202,12 @@ export class DynamicStandaloneServices extends Disposable { let contextViewService = ensure(IContextViewService, () => this._register(new ContextViewService(layoutService))); - ensure(IContextMenuService, () => this._register(new ContextMenuService(layoutService, telemetryService, notificationService, contextViewService, keybindingService, themeService))); + ensure(IContextMenuService, () => { + const contextMenuService = new ContextMenuService(telemetryService, notificationService, contextViewService, keybindingService, themeService); + contextMenuService.configure({ blockMouse: false }); // we do not want that in the standalone editor + + return this._register(contextMenuService); + }); ensure(IMenuService, () => new MenuService(commandService)); diff --git a/src/vs/editor/standalone/test/browser/simpleServices.test.ts b/src/vs/editor/standalone/test/browser/simpleServices.test.ts index 102a2e01f5..979d270d7e 100644 --- a/src/vs/editor/standalone/test/browser/simpleServices.test.ts +++ b/src/vs/editor/standalone/test/browser/simpleServices.test.ts @@ -39,7 +39,7 @@ suite('StandaloneKeybindingService', () => { let commandInvoked = false; keybindingService.addDynamicKeybinding('testCommand', KeyCode.F9, () => { commandInvoked = true; - }, null); + }, undefined); keybindingService.testDispatch({ _standardKeyboardEventBrand: true, diff --git a/src/vs/editor/test/browser/editorTestServices.ts b/src/vs/editor/test/browser/editorTestServices.ts index 3d1f006444..a00104b99c 100644 --- a/src/vs/editor/test/browser/editorTestServices.ts +++ b/src/vs/editor/test/browser/editorTestServices.ts @@ -13,7 +13,7 @@ import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class TestCodeEditorService extends AbstractCodeEditorService { - public lastInput: IResourceInput; + public lastInput?: IResourceInput; public getActiveCodeEditor(): ICodeEditor | null { return null; } public openCodeEditor(input: IResourceInput, source: ICodeEditor | null, sideBySide?: boolean): Promise { this.lastInput = input; diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index b571ad6b10..e9ca426999 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -30,7 +30,7 @@ suite('OpenerService', function () { test('delegate to editorService, scheme:///fff', function () { const openerService = new OpenerService(editorService, NullCommandService); openerService.open(URI.parse('another:///somepath')); - assert.equal(editorService.lastInput.options!.selection, undefined); + assert.equal(editorService.lastInput!.options!.selection, undefined); }); test('delegate to editorService, scheme:///fff#L123', function () { @@ -38,22 +38,22 @@ suite('OpenerService', function () { const openerService = new OpenerService(editorService, NullCommandService); openerService.open(URI.parse('file:///somepath#L23')); - assert.equal(editorService.lastInput.options!.selection!.startLineNumber, 23); - assert.equal(editorService.lastInput.options!.selection!.startColumn, 1); - assert.equal(editorService.lastInput.options!.selection!.endLineNumber, undefined); - assert.equal(editorService.lastInput.options!.selection!.endColumn, undefined); - assert.equal(editorService.lastInput.resource.fragment, ''); + assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); + assert.equal(editorService.lastInput!.options!.selection!.startColumn, 1); + assert.equal(editorService.lastInput!.options!.selection!.endLineNumber, undefined); + assert.equal(editorService.lastInput!.options!.selection!.endColumn, undefined); + assert.equal(editorService.lastInput!.resource.fragment, ''); openerService.open(URI.parse('another:///somepath#L23')); - assert.equal(editorService.lastInput.options!.selection!.startLineNumber, 23); - assert.equal(editorService.lastInput.options!.selection!.startColumn, 1); + assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); + assert.equal(editorService.lastInput!.options!.selection!.startColumn, 1); openerService.open(URI.parse('another:///somepath#L23,45')); - assert.equal(editorService.lastInput.options!.selection!.startLineNumber, 23); - assert.equal(editorService.lastInput.options!.selection!.startColumn, 45); - assert.equal(editorService.lastInput.options!.selection!.endLineNumber, undefined); - assert.equal(editorService.lastInput.options!.selection!.endColumn, undefined); - assert.equal(editorService.lastInput.resource.fragment, ''); + assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); + assert.equal(editorService.lastInput!.options!.selection!.startColumn, 45); + assert.equal(editorService.lastInput!.options!.selection!.endLineNumber, undefined); + assert.equal(editorService.lastInput!.options!.selection!.endColumn, undefined); + assert.equal(editorService.lastInput!.resource.fragment, ''); }); test('delegate to editorService, scheme:///fff#123,123', function () { @@ -61,18 +61,18 @@ suite('OpenerService', function () { const openerService = new OpenerService(editorService, NullCommandService); openerService.open(URI.parse('file:///somepath#23')); - assert.equal(editorService.lastInput.options!.selection!.startLineNumber, 23); - assert.equal(editorService.lastInput.options!.selection!.startColumn, 1); - assert.equal(editorService.lastInput.options!.selection!.endLineNumber, undefined); - assert.equal(editorService.lastInput.options!.selection!.endColumn, undefined); - assert.equal(editorService.lastInput.resource.fragment, ''); + assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); + assert.equal(editorService.lastInput!.options!.selection!.startColumn, 1); + assert.equal(editorService.lastInput!.options!.selection!.endLineNumber, undefined); + assert.equal(editorService.lastInput!.options!.selection!.endColumn, undefined); + assert.equal(editorService.lastInput!.resource.fragment, ''); openerService.open(URI.parse('file:///somepath#23,45')); - assert.equal(editorService.lastInput.options!.selection!.startLineNumber, 23); - assert.equal(editorService.lastInput.options!.selection!.startColumn, 45); - assert.equal(editorService.lastInput.options!.selection!.endLineNumber, undefined); - assert.equal(editorService.lastInput.options!.selection!.endColumn, undefined); - assert.equal(editorService.lastInput.resource.fragment, ''); + assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); + assert.equal(editorService.lastInput!.options!.selection!.startColumn, 45); + assert.equal(editorService.lastInput!.options!.selection!.endLineNumber, undefined); + assert.equal(editorService.lastInput!.options!.selection!.endColumn, undefined); + assert.equal(editorService.lastInput!.resource.fragment, ''); }); test('delegate to commandsService, command:someid', function () { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index d3d8225bf5..cc9ba4e61e 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5205,6 +5205,7 @@ declare namespace monaco.languages { * the formatting-feature. */ export interface DocumentFormattingEditProvider { + readonly displayName?: string; /** * Provide formatting edits for a whole document. */ @@ -5216,6 +5217,7 @@ declare namespace monaco.languages { * the formatting-feature. */ export interface DocumentRangeFormattingEditProvider { + readonly displayName?: string; /** * Provide formatting edits for a range in a document. * diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts index 4991691d86..99c1f5600c 100644 --- a/src/vs/platform/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -110,7 +110,7 @@ class Menu implements IMenu { const [id, items] = group; const activeActions: Array = []; for (const item of items) { - if (this._contextKeyService.contextMatchesRules(item.when || null)) { + if (this._contextKeyService.contextMatchesRules(item.when)) { const action = isIMenuItem(item) ? new MenuItemAction(item.command, item.alt, options, this._contextKeyService, this._commandService) : new SubmenuItemAction(item); activeActions.push(action); } diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index edff80c3b4..732cfcd0fc 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -454,7 +454,7 @@ export class Configuration { if (workspace && resource) { const root = workspace.getFolder(resource); if (root) { - return this._folderConfigurations.get(root.uri) || null; + return types.withUndefinedAsNull(this._folderConfigurations.get(root.uri)); } } return null; diff --git a/src/vs/platform/configuration/test/node/configurationService.test.ts b/src/vs/platform/configuration/test/node/configurationService.test.ts index fbd0dfa764..85a5089024 100644 --- a/src/vs/platform/configuration/test/node/configurationService.test.ts +++ b/src/vs/platform/configuration/test/node/configurationService.test.ts @@ -19,7 +19,7 @@ import { testFile } from 'vs/base/test/node/utils'; class SettingsTestEnvironmentService extends EnvironmentService { - constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome) { + constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome: string) { super(args, _execPath); } diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index b009f5d0f5..ca5d50cb20 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -252,7 +252,7 @@ export abstract class AbstractContextKeyService implements IContextKeyService { return new ScopedContextKeyService(this, this._onDidChangeContextKey, domNode); } - public contextMatchesRules(rules: ContextKeyExpr | null): boolean { + public contextMatchesRules(rules: ContextKeyExpr | undefined): boolean { if (this._isDisposed) { throw new Error(`AbstractContextKeyService has been disposed`); } diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 871b34b3d9..5d742b0d6f 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -58,14 +58,15 @@ export abstract class ContextKeyExpr { public static greaterThanEquals(key: string, value: any): ContextKeyExpr { return new ContextKeyGreaterThanEqualsExpr(key, value); } + public static lessThanEquals(key: string, value: any): ContextKeyExpr { return new ContextKeyLessThanEqualsExpr(key, value); } // - public static deserialize(serialized: string | null | undefined, strict: boolean = false): ContextKeyExpr | null { + public static deserialize(serialized: string | null | undefined, strict: boolean = false): ContextKeyExpr | undefined { if (!serialized) { - return null; + return undefined; } let pieces = serialized.split('&&'); @@ -167,7 +168,7 @@ export abstract class ContextKeyExpr { public abstract getType(): ContextKeyExprType; public abstract equals(other: ContextKeyExpr): boolean; public abstract evaluate(context: IContext): boolean; - public abstract normalize(): ContextKeyExpr | null; + public abstract normalize(): ContextKeyExpr | undefined; public abstract serialize(): string; public abstract keys(): string[]; public abstract map(mapFnc: IContextKeyExprMapper): ContextKeyExpr; @@ -549,9 +550,9 @@ export class ContextKeyAndExpr implements ContextKeyExpr { return expr; } - public normalize(): ContextKeyExpr | null { + public normalize(): ContextKeyExpr | undefined { if (this.expr.length === 0) { - return null; + return undefined; } if (this.expr.length === 1) { @@ -762,7 +763,7 @@ export interface IContextKeyService { onDidChangeContext: Event; createKey(key: string, defaultValue: T | undefined): IContextKey; - contextMatchesRules(rules: ContextKeyExpr | null): boolean; + contextMatchesRules(rules: ContextKeyExpr | undefined): boolean; getContextKeyValue(key: string): T | undefined; createScoped(target?: IContextKeyServiceTarget): IContextKeyService; diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index 0b2647e628..c3eb560e05 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -5,50 +5,38 @@ import 'vs/css!./contextMenuHandler'; -import { combinedDisposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle'; import { ActionRunner, IRunEvent } from 'vs/base/common/actions'; import { Menu } from 'vs/base/browser/ui/menu/menu'; - import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; -import { addDisposableListener, EventType, $, removeNode } from 'vs/base/browser/dom'; +import { EventType, $, removeNode } from 'vs/base/browser/dom'; import { attachMenuStyler } from 'vs/platform/theme/common/styler'; import { domEvent } from 'vs/base/browser/event'; -import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; + +export interface IContextMenuHandlerOptions { + blockMouse: boolean; +} export class ContextMenuHandler { - private element: HTMLElement | null; - private elementDisposable: IDisposable; - private menuContainerElement: HTMLElement | null; private focusToReturn: HTMLElement; private block: HTMLElement | null; + private options: IContextMenuHandlerOptions = { blockMouse: true }; constructor( - private layoutService: ILayoutService, private contextViewService: IContextViewService, private telemetryService: ITelemetryService, private notificationService: INotificationService, private keybindingService: IKeybindingService, private themeService: IThemeService - ) { - this.setContainer(this.layoutService.container); - } + ) { } - setContainer(container: HTMLElement | null): void { - if (this.element) { - this.elementDisposable = dispose(this.elementDisposable); - this.element = null; - } - - if (container) { - this.element = container; - this.elementDisposable = addDisposableListener(this.element, EventType.MOUSE_DOWN, (e) => this.onMouseDown(e as MouseEvent)); - } + configure(options: IContextMenuHandlerOptions): void { + this.options = options; } showContextMenu(delegate: IContextMenuDelegate): void { @@ -67,8 +55,6 @@ export class ContextMenuHandler { anchorAlignment: delegate.anchorAlignment, render: (container) => { - this.menuContainerElement = container; - let className = delegate.getMenuClassName ? delegate.getMenuClassName() : ''; if (className) { @@ -76,7 +62,7 @@ export class ContextMenuHandler { } // Render invisible div to block mouse interaction in the rest of the UI - if (this.layoutService.hasWorkbench) { + if (this.options.blockMouse) { this.block = container.appendChild($('.context-view-block')); } @@ -120,8 +106,6 @@ export class ContextMenuHandler { if (this.focusToReturn) { this.focusToReturn.focus(); } - - this.menuContainerElement = null; } }); } @@ -150,27 +134,4 @@ export class ContextMenuHandler { this.notificationService.error(e.error); } } - - private onMouseDown(e: MouseEvent): void { - if (!this.menuContainerElement) { - return; - } - - let event = new StandardMouseEvent(e); - let element: HTMLElement | null = event.target; - - while (element) { - if (element === this.menuContainerElement) { - return; - } - - element = element.parentElement; - } - - this.contextViewService.hideContextView(); - } - - dispose(): void { - this.setContainer(null); - } } diff --git a/src/vs/platform/contextview/browser/contextMenuService.ts b/src/vs/platform/contextview/browser/contextMenuService.ts index dbdb8d4f23..216a2f4a1b 100644 --- a/src/vs/platform/contextview/browser/contextMenuService.ts +++ b/src/vs/platform/contextview/browser/contextMenuService.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ContextMenuHandler } from './contextMenuHandler'; +import { ContextMenuHandler, IContextMenuHandlerOptions } from './contextMenuHandler'; import { IContextViewService, IContextMenuService } from './contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Event, Emitter } from 'vs/base/common/event'; @@ -12,7 +12,6 @@ import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Disposable } from 'vs/base/common/lifecycle'; -import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; export class ContextMenuService extends Disposable implements IContextMenuService { _serviceBrand: any; @@ -23,7 +22,6 @@ export class ContextMenuService extends Disposable implements IContextMenuServic private contextMenuHandler: ContextMenuHandler; constructor( - @ILayoutService layoutService: ILayoutService, @ITelemetryService telemetryService: ITelemetryService, @INotificationService notificationService: INotificationService, @IContextViewService contextViewService: IContextViewService, @@ -32,15 +30,11 @@ export class ContextMenuService extends Disposable implements IContextMenuServic ) { super(); - this.contextMenuHandler = this._register(new ContextMenuHandler(layoutService, contextViewService, telemetryService, notificationService, keybindingService, themeService)); + this.contextMenuHandler = new ContextMenuHandler(contextViewService, telemetryService, notificationService, keybindingService, themeService); } - dispose(): void { - this.contextMenuHandler.dispose(); - } - - setContainer(container: HTMLElement): void { - this.contextMenuHandler.setContainer(container); + configure(options: IContextMenuHandlerOptions): void { + this.contextMenuHandler.configure(options); } // ContextMenu diff --git a/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts b/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts index 5b72e342d7..bd5e909bcd 100644 --- a/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts @@ -391,7 +391,7 @@ function collectWorkspaceStats(folder: string, filter: string[]): Promise void): void { + function walk(dir: string, filter: string[], token: { count: any; maxReached: any; }, done: (allFiles: string[]) => void): void { let results: string[] = []; readdir(dir, async (err, files) => { // Ignore folders that can't be read diff --git a/src/vs/platform/environment/test/node/environmentService.test.ts b/src/vs/platform/environment/test/node/environmentService.test.ts index 08c751d26b..56afce09ed 100644 --- a/src/vs/platform/environment/test/node/environmentService.test.ts +++ b/src/vs/platform/environment/test/node/environmentService.test.ts @@ -11,7 +11,7 @@ import { parseExtensionHostPort, parseUserDataDir } from 'vs/platform/environmen suite('EnvironmentService', () => { test('parseExtensionHostPort when built', () => { - const parse = a => parseExtensionHostPort(parseArgs(a), true); + const parse = (a: string[]) => parseExtensionHostPort(parseArgs(a), true); assert.deepEqual(parse([]), { port: null, break: false, debugId: undefined }); assert.deepEqual(parse(['--debugPluginHost']), { port: null, break: false, debugId: undefined }); @@ -28,7 +28,7 @@ suite('EnvironmentService', () => { }); test('parseExtensionHostPort when unbuilt', () => { - const parse = a => parseExtensionHostPort(parseArgs(a), false); + const parse = (a: string[]) => parseExtensionHostPort(parseArgs(a), false); assert.deepEqual(parse([]), { port: 5870, break: false, debugId: undefined }); assert.deepEqual(parse(['--debugPluginHost']), { port: 5870, break: false, debugId: undefined }); @@ -45,7 +45,7 @@ suite('EnvironmentService', () => { }); test('userDataPath', () => { - const parse = (a, b: { cwd: () => string, env: { [key: string]: string } }) => parseUserDataDir(parseArgs(a), b); + const parse = (a: string[], b: { cwd: () => string, env: { [key: string]: string } }) => parseUserDataDir(parseArgs(a), b); assert.equal(parse(['--user-data-dir', './dir'], { cwd: () => '/foo', env: {} }), path.resolve('/foo/dir'), 'should use cwd when --user-data-dir is specified'); diff --git a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts index a73238c001..b21c8851fb 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts @@ -26,7 +26,7 @@ export function getGalleryExtensionId(publisher: string, name: string): string { export function groupByExtension(extensions: T[], getExtensionIdentifier: (t: T) => IExtensionIdentifier): T[][] { const byExtension: T[][] = []; - const findGroup = extension => { + const findGroup = (extension: T) => { for (const group of byExtension) { if (group.some(e => areSameExtensions(getExtensionIdentifier(e), getExtensionIdentifier(extension)))) { return group; diff --git a/src/vs/platform/extensionManagement/common/extensionNls.ts b/src/vs/platform/extensionManagement/common/extensionNls.ts index 47ed8bde52..f098991a8d 100644 --- a/src/vs/platform/extensionManagement/common/extensionNls.ts +++ b/src/vs/platform/extensionManagement/common/extensionNls.ts @@ -13,7 +13,7 @@ export interface ITranslations { } export function localizeManifest(manifest: IExtensionManifest, translations: ITranslations): IExtensionManifest { - const patcher = value => { + const patcher = (value: string) => { if (typeof value !== 'string') { return undefined; } diff --git a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts index b3bbe04116..1bf0c68798 100644 --- a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts +++ b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts @@ -54,7 +54,7 @@ export class ExtensionsLifecycle extends Disposable { return new Promise((c, e) => { const extensionLifecycleProcess = this.start(lifecycleHook, lifecycleType, args, extension); - let timeoutHandler; + let timeoutHandler: any; const onexit = (error?: string) => { if (timeoutHandler) { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts index 97ca819612..665995ea2a 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts @@ -44,7 +44,7 @@ export class ExtensionManagementChannel implements IServerChannel { this.onDidUninstallExtension = Event.buffer(service.onDidUninstallExtension, true); } - listen(context, event: string): Event { + listen(context: any, event: string): Event { const uriTransformer = this.getUriTransformer(context); switch (event) { case 'onInstallExtension': return this.onInstallExtension; @@ -56,7 +56,7 @@ export class ExtensionManagementChannel implements IServerChannel { throw new Error('Invalid listen'); } - call(context, command: string, args?: any): Promise { + call(context: any, command: string, args?: any): Promise { const uriTransformer: IURITransformer | null = this.getUriTransformer(context); switch (command) { case 'zip': return this.service.zip(transformIncomingExtension(args[0], uriTransformer)).then(uri => transformOutgoingURI(uri, uriTransformer)); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 47510c3e97..9f2477c459 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -170,7 +170,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private collectFiles(extension: ILocalExtension): Promise { - const collectFilesFromDirectory = async (dir): Promise => { + const collectFilesFromDirectory = async (dir: string): Promise => { let entries = await pfs.readdir(dir); entries = entries.map(e => path.join(dir, e)); const stats = await Promise.all(entries.map(e => pfs.stat(e))); @@ -288,7 +288,7 @@ export class ExtensionManagementService extends Disposable implements IExtension this.reportTelemetry(this.getTelemetryEvent(operation), getGalleryExtensionTelemetryData(extension), new Date().getTime() - startTime, undefined); }; - const onDidInstallExtensionFailure = (extension: IGalleryExtension, operation: InstallOperation, error) => { + const onDidInstallExtensionFailure = (extension: IGalleryExtension, operation: InstallOperation, error: Error) => { const errorCode = error && (error).code ? (error).code : ERROR_UNKNOWN; this.logService.error(`Failed to install extension:`, extension.identifier.id, error ? error.message : errorCode); this._onDidInstallExtension.fire({ identifier: extension.identifier, gallery: extension, operation, error: errorCode }); diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 7c7f952e7a..1fa830a770 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -84,13 +84,18 @@ export interface IFileService { * If the optional parameter "resolveSingleChildDescendants" is specified in options, * the stat service is asked to automatically resolve child folders that only * contain a single element. + * + * If the optional parameter "resolveMetadata" is specified in options, + * the stat will contain metadata information such as size, mtime and etag. */ + resolveFile(resource: URI, options: IResolveMetadataFileOptions): Promise; resolveFile(resource: URI, options?: IResolveFileOptions): Promise; /** * Same as resolveFile but supports resolving multiple resources in parallel. * If one of the resolve targets fails to resolve returns a fake IFileStat instead of making the whole call fail. */ + resolveFiles(toResolve: { resource: URI, options: IResolveMetadataFileOptions }[]): Promise; resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise; /** @@ -115,21 +120,21 @@ export interface IFileService { /** * Updates the content replacing its previous value. */ - updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise; + updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise; /** * Moves the file to a new path identified by the resource. * * The optional parameter overwrite can be set to replace an existing file at the location. */ - moveFile(source: URI, target: URI, overwrite?: boolean): Promise; + moveFile(source: URI, target: URI, overwrite?: boolean): Promise; /** * Copies the file to a path identified by the resource. * * The optional parameter overwrite can be set to replace an existing file at the location. */ - copyFile(source: URI, target: URI, overwrite?: boolean): Promise; + copyFile(source: URI, target: URI, overwrite?: boolean): Promise; /** * Creates a new file with the given path. The returned promise @@ -137,13 +142,13 @@ export interface IFileService { * * The optional parameter content can be used as value to fill into the new file. */ - createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise; + createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise; /** * Creates a new folder with the given path. The returned promise * will have the stat model object as a result. */ - createFolder(resource: URI): Promise; + createFolder(resource: URI): Promise; /** * Deletes the provided file. The optional useTrash parameter allows to @@ -194,9 +199,9 @@ export enum FileType { export interface IStat { type: FileType; - mtime: number; - ctime: number; - size: number; + mtime?: number; + ctime?: number; + size?: number; } export interface IWatchOptions { @@ -329,7 +334,7 @@ export const enum FileOperation { export class FileOperationEvent { - constructor(private _resource: URI, private _operation: FileOperation, private _target?: IFileStat) { + constructor(private _resource: URI, private _operation: FileOperation, private _target?: IFileStatWithMetadata) { } get resource(): URI { @@ -481,7 +486,7 @@ export function isParent(path: string, candidate: string, ignoreCase?: boolean): return path.indexOf(candidate) === 0; } -export interface IBaseStat { +interface IBaseStat { /** * The unified resource identifier of this file or folder. @@ -494,15 +499,29 @@ export interface IBaseStat { */ name: string; + /** + * The size of the file. + * + * The value may or may not be resolved as + * it is optional. + */ + size?: number; + /** * The last modifictaion date represented * as millis from unix epoch. + * + * The value may or may not be resolved as + * it is optional. */ - mtime: number; + mtime?: number; /** * A unique identifier thet represents the * current state of the file or directory. + * + * The value may or may not be resolved as + * it is optional. */ etag?: string; @@ -512,6 +531,12 @@ export interface IBaseStat { isReadonly?: boolean; } +export interface IBaseStatWithMetadata extends IBaseStat { + mtime: number; + etag: string; + size: number; +} + /** * A file resource with meta information. */ @@ -532,11 +557,13 @@ export interface IFileStat extends IBaseStat { * The children of the file stat or undefined if none. */ children?: IFileStat[]; +} - /** - * The size of the file if known. - */ - size?: number; +export interface IFileStatWithMetadata extends IFileStat, IBaseStatWithMetadata { + mtime: number; + etag: string; + size: number; + children?: IFileStatWithMetadata[]; } export interface IResolveFileResult { @@ -544,10 +571,14 @@ export interface IResolveFileResult { success: boolean; } +export interface IResolveFileResultWithMetadata extends IResolveFileResult { + stat?: IFileStatWithMetadata; +} + /** * Content and meta information of a file. */ -export interface IContent extends IBaseStat { +export interface IContent extends IBaseStatWithMetadata { /** * The content of a text file. @@ -614,7 +645,7 @@ export function snapshotToString(snapshot: ITextSnapshot): string { /** * Streamable content and meta information of a file. */ -export interface IStreamContent extends IBaseStat { +export interface IStreamContent extends IBaseStatWithMetadata { /** * The streamable content of a text file. @@ -712,6 +743,16 @@ export interface IResolveFileOptions { * Automatically continue resolving children of a directory if the number of children is 1. */ resolveSingleChildDescendants?: boolean; + + /** + * Will resolve mtime, size and etag of files if enabled. This can have a negative impact + * on performance and thus should only be used when these values are required. + */ + resolveMetadata?: boolean; +} + +export interface IResolveMetadataFileOptions extends IResolveFileOptions { + resolveMetadata: true; } export interface ICreateFileOptions { @@ -1033,6 +1074,17 @@ export enum FileKind { export const MIN_MAX_MEMORY_SIZE_MB = 2048; export const FALLBACK_MAX_MEMORY_SIZE_MB = 4096; +export function etag(mtime: number, size: number): string; +export function etag(mtime: number | undefined, size: number | undefined): string | undefined; +export function etag(mtime: number | undefined, size: number | undefined): string | undefined { + if (typeof size !== 'number' || typeof mtime !== 'number') { + return undefined; + } + + return mtime.toString(29) + size.toString(31); +} + + // TODO@ben remove traces of legacy file service export const ILegacyFileService = createDecorator('legacyFileService'); export interface ILegacyFileService { @@ -1049,10 +1101,6 @@ export interface ILegacyFileService { updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise; - moveFile(source: URI, target: URI, overwrite?: boolean): Promise; - - copyFile(source: URI, target: URI, overwrite?: boolean): Promise; - createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise; del(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise; diff --git a/src/vs/platform/instantiation/test/common/instantiationService.test.ts b/src/vs/platform/instantiation/test/common/instantiationService.test.ts index 5bff03ebfe..49a6888fbc 100644 --- a/src/vs/platform/instantiation/test/common/instantiationService.test.ts +++ b/src/vs/platform/instantiation/test/common/instantiationService.test.ts @@ -71,7 +71,7 @@ class Service1Consumer { class Target2Dep { - constructor(@IService1 service1: IService1, @IService2 service2) { + constructor(@IService1 service1: IService1, @IService2 service2: Service2) { assert.ok(service1 instanceof Service1); assert.ok(service2 instanceof Service2); } @@ -99,7 +99,7 @@ class TargetOptional { } class DependentServiceTarget { - constructor(@IDependentService d) { + constructor(@IDependentService d: IDependentService) { assert.ok(d); assert.equal(d.name, 'farboo'); } diff --git a/src/vs/platform/issue/electron-main/issueService.ts b/src/vs/platform/issue/electron-main/issueService.ts index 4c2998f319..ef6a65cd36 100644 --- a/src/vs/platform/issue/electron-main/issueService.ts +++ b/src/vs/platform/issue/electron-main/issueService.ts @@ -72,7 +72,7 @@ export class IssueService implements IIssueService { } }); - ipcMain.on('vscode:workbenchCommand', (_: unknown, commandInfo) => { + ipcMain.on('vscode:workbenchCommand', (_: unknown, commandInfo: { id: any; from: any; args: any; }) => { const { id, from, args } = commandInfo; let parentWindow: BrowserWindow | null; @@ -92,7 +92,7 @@ export class IssueService implements IIssueService { } }); - ipcMain.on('vscode:openExternal', (_: unknown, arg) => { + ipcMain.on('vscode:openExternal', (_: unknown, arg: string) => { this.windowsService.openExternal(arg); }); diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index f8680b7349..aab14bb1c3 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -50,7 +50,7 @@ export class KeybindingResolver { } } - private static _isTargetedForRemoval(defaultKb: ResolvedKeybindingItem, keypressFirstPart: string | null, keypressChordPart: string | null, command: string, when: ContextKeyExpr | null): boolean { + private static _isTargetedForRemoval(defaultKb: ResolvedKeybindingItem, keypressFirstPart: string | null, keypressChordPart: string | null, command: string, when: ContextKeyExpr | undefined): boolean { if (defaultKb.command !== command) { return false; } @@ -172,7 +172,7 @@ export class KeybindingResolver { * Returns true if it is provable `a` implies `b`. * **Precondition**: Assumes `a` and `b` are normalized! */ - public static whenIsEntirelyIncluded(a: ContextKeyExpr | null, b: ContextKeyExpr | null): boolean { + public static whenIsEntirelyIncluded(a: ContextKeyExpr | null | undefined, b: ContextKeyExpr | null | undefined): boolean { if (!b) { return true; } @@ -304,7 +304,7 @@ export class KeybindingResolver { return null; } - public static contextMatchesRules(context: IContext, rules: ContextKeyExpr | null): boolean { + public static contextMatchesRules(context: IContext, rules: ContextKeyExpr | null | undefined): boolean { if (!rules) { return true; } diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index b78a26b6f4..8d32769f8b 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -49,7 +49,7 @@ export interface IKeybindingRule2 { id: string; args?: any; weight: number; - when: ContextKeyExpr | null; + when: ContextKeyExpr | undefined; } export const enum KeybindingWeight { diff --git a/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts b/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts index 6ce44fd688..084a314b8e 100644 --- a/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts +++ b/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts @@ -15,10 +15,10 @@ export class ResolvedKeybindingItem { public readonly bubble: boolean; public readonly command: string | null; public readonly commandArgs: any; - public readonly when: ContextKeyExpr | null; + public readonly when: ContextKeyExpr | undefined; public readonly isDefault: boolean; - constructor(resolvedKeybinding: ResolvedKeybinding | null, command: string | null, commandArgs: any, when: ContextKeyExpr | null, isDefault: boolean) { + constructor(resolvedKeybinding: ResolvedKeybinding | null, command: string | null, commandArgs: any, when: ContextKeyExpr | undefined, isDefault: boolean) { this.resolvedKeybinding = resolvedKeybinding; this.keypressParts = resolvedKeybinding ? removeElementsAfterNulls(resolvedKeybinding.getDispatchParts()) : []; this.bubble = (command ? command.charCodeAt(0) === CharCode.Caret : false); diff --git a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts index 777ab2d2aa..11fc59759f 100644 --- a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts @@ -178,7 +178,7 @@ suite('AbstractKeybindingService', () => { statusMessageCallsDisposed = null; }); - function kbItem(keybinding: number, command: string, when: ContextKeyExpr | null = null): ResolvedKeybindingItem { + function kbItem(keybinding: number, command: string, when?: ContextKeyExpr): ResolvedKeybindingItem { const resolvedKeybinding = (keybinding !== 0 ? new USLayoutResolvedKeybinding(createKeybinding(keybinding, OS)!, OS) : null); return new ResolvedKeybindingItem( resolvedKeybinding, diff --git a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts index 15b318b08c..e44c624060 100644 --- a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts @@ -26,7 +26,7 @@ suite('KeybindingResolver', () => { resolvedKeybinding, command, commandArgs, - when ? when.normalize() : null, + when ? when.normalize() : undefined, isDefault ); } diff --git a/src/vs/platform/launch/electron-main/launchService.ts b/src/vs/platform/launch/electron-main/launchService.ts index ed53689bb4..63d9450f92 100644 --- a/src/vs/platform/launch/electron-main/launchService.ts +++ b/src/vs/platform/launch/electron-main/launchService.ts @@ -215,7 +215,8 @@ export class LaunchService implements ILaunchService { preferNewWindow: !args['reuse-window'] && !args.wait, forceReuseWindow: args['reuse-window'], diffMode: args.diff, - addMode: args.add + addMode: args.add, + noRecentEntry: !!args['skip-add-to-recently-opened'] }); } diff --git a/src/vs/platform/layout/browser/layoutService.ts b/src/vs/platform/layout/browser/layoutService.ts index 17bc5d5f50..59053d9ef5 100644 --- a/src/vs/platform/layout/browser/layoutService.ts +++ b/src/vs/platform/layout/browser/layoutService.ts @@ -32,10 +32,4 @@ export interface ILayoutService { * event carries the dimensions of the container as part of it. */ readonly onLayout: Event; - - - /** - * Indicates if the layout has a workbench surrounding the editor - */ - readonly hasWorkbench: boolean; } diff --git a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts index b000efc918..a317d25fde 100644 --- a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts +++ b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts @@ -56,7 +56,7 @@ export class LifecycleService extends AbstractLifecycleService { const windowId = this.windowService.getCurrentWindowId(); // Main side indicates that window is about to unload, check for vetos - ipc.on('vscode:onBeforeUnload', (event, reply: { okChannel: string, cancelChannel: string, reason: ShutdownReason }) => { + ipc.on('vscode:onBeforeUnload', (_event: unknown, reply: { okChannel: string, cancelChannel: string, reason: ShutdownReason }) => { this.logService.trace(`lifecycle: onBeforeUnload (reason: ${reply.reason})`); // trigger onBeforeShutdown events and veto collecting @@ -75,7 +75,7 @@ export class LifecycleService extends AbstractLifecycleService { }); // Main side indicates that we will indeed shutdown - ipc.on('vscode:onWillUnload', (event, reply: { replyChannel: string, reason: ShutdownReason }) => { + ipc.on('vscode:onWillUnload', (_event: unknown, reply: { replyChannel: string, reason: ShutdownReason }) => { this.logService.trace(`lifecycle: onWillUnload (reason: ${reply.reason})`); // trigger onWillShutdown events and joining diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts index cbbbb43ba0..ab3b3cd757 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts @@ -276,7 +276,7 @@ export class LifecycleService extends Disposable implements ILifecycleService { }); // Window After Closing - window.win.on('closed', e => { + window.win.on('closed', () => { this.logService.trace(`Lifecycle#window.on('closed') - window ID ${window.id}`); // update window count diff --git a/src/vs/platform/remote/common/remoteAgentEnvironment.ts b/src/vs/platform/remote/common/remoteAgentEnvironment.ts index cd08fca8b8..814a11e67d 100644 --- a/src/vs/platform/remote/common/remoteAgentEnvironment.ts +++ b/src/vs/platform/remote/common/remoteAgentEnvironment.ts @@ -11,6 +11,7 @@ export interface IRemoteAgentEnvironment { pid: number; appRoot: URI; appSettingsHome: URI; + appSettingsPath: URI; logsPath: URI; extensionsPath: URI; extensionHostLogsPath: URI; diff --git a/src/vs/platform/storage/test/node/storageService.test.ts b/src/vs/platform/storage/test/node/storageService.test.ts index 86fd8d5d54..c84e73d2d5 100644 --- a/src/vs/platform/storage/test/node/storageService.test.ts +++ b/src/vs/platform/storage/test/node/storageService.test.ts @@ -85,7 +85,7 @@ suite('StorageService', () => { test('Migrate Data', async () => { class StorageTestEnvironmentService extends EnvironmentService { - constructor(private workspaceStorageFolderPath: string, private _extensionsPath) { + constructor(private workspaceStorageFolderPath: string, private _extensionsPath: string) { super(parseArgs(process.argv), process.execPath); } diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index 4c6a7331ba..dcf70f5e48 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -326,6 +326,6 @@ export const defaultMenuStyles = { separatorColor: menuSeparatorBackground }; -export function attachMenuStyler(widget: IThemable, themeService, style?: IMenuStyleOverrides): IDisposable { +export function attachMenuStyler(widget: IThemable, themeService: IThemeService, style?: IMenuStyleOverrides): IDisposable { return attachStyler(themeService, { ...defaultMenuStyles, ...style }, widget); } diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index e8b9f43826..c3fc7db3b9 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -182,6 +182,7 @@ export interface IOpenSettings { forceOpenWorkspaceAsFile?: boolean; diffMode?: boolean; addMode?: boolean; + noRecentEntry?: boolean; args?: ParsedArgs; } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 36375262ee..629ececbea 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -134,6 +134,7 @@ export interface IOpenConfiguration { addMode?: boolean; readonly forceOpenWorkspaceAsFile?: boolean; readonly initialStartup?: boolean; + readonly noRecentEntry?: boolean; } export interface ISharedProcess { diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 7735f0c09a..dbed5a8a53 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -273,7 +273,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable }); } - async openWindow(windowId: number, urisToOpen: IURIToOpen[], options?: IOpenSettings): Promise { + async openWindow(windowId: number, urisToOpen: IURIToOpen[], options: IOpenSettings = {}): Promise { this.logService.trace('windowsService#openWindow'); if (!urisToOpen || !urisToOpen.length) { return undefined; @@ -283,12 +283,13 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable context: OpenContext.API, contextWindowId: windowId, urisToOpen: urisToOpen, - cli: options && options.args ? { ...this.environmentService.args, ...options.args } : this.environmentService.args, - forceNewWindow: options && options.forceNewWindow, - forceReuseWindow: options && options.forceReuseWindow, - forceOpenWorkspaceAsFile: options && options.forceOpenWorkspaceAsFile, - diffMode: options && options.diffMode, - addMode: options && options.addMode + cli: options.args ? { ...this.environmentService.args, ...options.args } : this.environmentService.args, + forceNewWindow: options.forceNewWindow, + forceReuseWindow: options.forceReuseWindow, + forceOpenWorkspaceAsFile: options.forceOpenWorkspaceAsFile, + diffMode: options.diffMode, + addMode: options.addMode, + noRecentEntry: options.noRecentEntry }); } diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index 9c7b830f05..5386bf9515 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -64,7 +64,7 @@ suite('WorkspacesMainService', () => { return pfs.del(untitledWorkspacesHomePath, os.tmpdir()); }); - function assertPathEquals(p1: string, p2): void { + function assertPathEquals(p1: string, p2: string): void { if (isWindows) { p1 = normalizeDriveLetter(p1); p2 = normalizeDriveLetter(p2); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 2c4ccf43e7..9d6fd67156 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1378,4 +1378,35 @@ declare module 'vscode' { group?: string; } //#endregion + + //#region Webview Port mapping— mjbvz + /** + * Defines a port mapping used for localhost inside the webview. + */ + export interface WebviewPortMapping { + /** + * Localhost port to remap inside the webview. + */ + readonly port: number; + + /** + * Destination port. The `port` is resolved to this port. + */ + readonly resolvedPort: number; + } + + export interface WebviewOptions { + /** + * Mappings of localhost ports used inside the webview. + * + * Port mapping allow webviews to transparently define how localhost ports are resolved. This can be used + * to allow using a static localhost port inside the webview that is resolved to random port that a service is + * running on. + * + * If a webview accesses localhost content, we recomend that you specify port mappings even if + * the `from` and `to` ports are the same. + */ + readonly portMapping?: ReadonlyArray; + } + //#endregion } diff --git a/src/vs/workbench/api/browser/mainThreadHeapService.ts b/src/vs/workbench/api/browser/mainThreadHeapService.ts index 73719e58d5..aa4b91945d 100644 --- a/src/vs/workbench/api/browser/mainThreadHeapService.ts +++ b/src/vs/workbench/api/browser/mainThreadHeapService.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { ExtHostContext, IExtHostContext } from '../common/extHost.protocol'; diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index d7acd4bf82..c6a089bff9 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -288,18 +288,20 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha // --- formatting - $registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier): void { + $registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier, displayName: string): void { this._registrations[handle] = modes.DocumentFormattingEditProviderRegistry.register(selector, { extensionId, + displayName, provideDocumentFormattingEdits: (model: ITextModel, options: modes.FormattingOptions, token: CancellationToken): Promise => { return this._proxy.$provideDocumentFormattingEdits(handle, model.uri, options, token); } }); } - $registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier): void { + $registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier, displayName: string): void { this._registrations[handle] = modes.DocumentRangeFormattingEditProviderRegistry.register(selector, { extensionId, + displayName, provideDocumentRangeFormattingEdits: (model: ITextModel, range: EditorRange, options: modes.FormattingOptions, token: CancellationToken): Promise => { return this._proxy.$provideDocumentRangeFormattingEdits(handle, model.uri, range, options, token); } diff --git a/src/vs/workbench/api/browser/mainThreadOutputService.ts b/src/vs/workbench/api/browser/mainThreadOutputService.ts index 5be21059dd..6953d67357 100644 --- a/src/vs/workbench/api/browser/mainThreadOutputService.ts +++ b/src/vs/workbench/api/browser/mainThreadOutputService.ts @@ -38,7 +38,7 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut const setVisibleChannel = () => { const panel = this._panelService.getActivePanel(); - const visibleChannel: IOutputChannel | null = panel && panel.getId() === OUTPUT_PANEL_ID ? this._outputService.getActiveChannel() : null; + const visibleChannel = panel && panel.getId() === OUTPUT_PANEL_ID ? this._outputService.getActiveChannel() : undefined; this._proxy.$setVisibleChannel(visibleChannel ? visibleChannel.id : null); }; this._register(Event.any(this._outputService.onActiveOutputChannel, this._panelService.onDidPanelOpen, this._panelService.onDidPanelClose)(() => setVisibleChannel())); @@ -104,7 +104,7 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut return undefined; } - private _getChannel(channelId: string): IOutputChannel | null { + private _getChannel(channelId: string): IOutputChannel | undefined { return this._outputService.getChannel(channelId); } } diff --git a/src/vs/workbench/api/browser/mainThreadQuickOpen.ts b/src/vs/workbench/api/browser/mainThreadQuickOpen.ts index b003b1860d..28f35cc28d 100644 --- a/src/vs/workbench/api/browser/mainThreadQuickOpen.ts +++ b/src/vs/workbench/api/browser/mainThreadQuickOpen.ts @@ -171,14 +171,14 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { } } else if (param === 'items') { handlesToItems.clear(); - params[param].forEach(item => { + params[param].forEach((item: TransferQuickPickItems) => { handlesToItems.set(item.handle, item); }); input[param] = params[param]; } else if (param === 'activeItems' || param === 'selectedItems') { input[param] = params[param] - .filter(handle => handlesToItems.has(handle)) - .map(handle => handlesToItems.get(handle)); + .filter((handle: number) => handlesToItems.has(handle)) + .map((handle: number) => handlesToItems.get(handle)); } else if (param === 'buttons') { input[param] = params.buttons!.map(button => { if (button.handle === -1) { diff --git a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts index 0dc3eaea4e..90ed1602b9 100644 --- a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts @@ -3,11 +3,10 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IdleValue, sequence } from 'vs/base/common/async'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import * as strings from 'vs/base/common/strings'; -import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand'; @@ -16,15 +15,13 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; -import { CodeAction, TextEdit } from 'vs/editor/common/modes'; -import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { ITextModel } from 'vs/editor/common/model'; +import { CodeAction } from 'vs/editor/common/modes'; import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; -import { getDocumentFormattingEdits, FormatMode } from 'vs/editor/contrib/format/format'; -import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit'; +import { getRealAndSyntheticDocumentFormattersOrdered, formatDocumentWithProvider } from 'vs/editor/contrib/format/format'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { localize } from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -37,7 +34,6 @@ import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textF // {{SQL CARBON EDIT}} import { ISaveParticipant, SaveReason, IResolvedTextFileEditorModel, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../common/extHost.protocol'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; // {{SQL CARBON EDIT}} import { INotebookService } from 'sql/workbench/services/notebook/common/notebookService'; @@ -243,10 +239,8 @@ export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant class FormatOnSaveParticipant implements ISaveParticipantParticipant { constructor( - @ICodeEditorService private readonly _editorService: ICodeEditorService, - @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { // Nothing } @@ -254,64 +248,34 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { const model = editorModel.textEditorModel; - if (env.reason === SaveReason.AUTO - || !this._configurationService.getValue('editor.formatOnSave', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() })) { + const overrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri }; + + if (env.reason === SaveReason.AUTO || !this._configurationService.getValue('editor.formatOnSave', overrides)) { return undefined; } - const versionNow = model.getVersionId(); - - const timeout = this._configurationService.getValue('editor.formatOnSaveTimeout', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() }); - - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const source = new CancellationTokenSource(); - const request = getDocumentFormattingEdits(this._telemetryService, this._editorWorkerService, model, model.getFormattingOptions(), FormatMode.Auto, source.token); - setTimeout(() => { - reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout)); - source.cancel(); - }, timeout); + const provider = getRealAndSyntheticDocumentFormattersOrdered(model); + if (provider.length !== 1) { + // print message for >1 case? + resolve(); - request.then(resolve, reject); + } else { + // having 1 formatter -> go for it + const timeout = this._configurationService.getValue('editor.formatOnSaveTimeout', overrides); + const request = this._instantiationService.invokeFunction(formatDocumentWithProvider, provider[0], model, source.token); - }).then(edits => { - if (isNonEmptyArray(edits) && versionNow === model.getVersionId()) { - const editor = findEditor(model, this._editorService); - if (editor) { - this._editsWithEditor(editor, edits); - } else { - this._editWithModel(model, edits); - } + setTimeout(() => { + reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout)); + source.cancel(); + }, timeout); + + request.then(resolve, reject); } }); } - - private _editsWithEditor(editor: ICodeEditor, edits: TextEdit[]): void { - FormattingEdit.execute(editor, edits); - } - - private _editWithModel(model: ITextModel, edits: TextEdit[]): void { - - const [{ range }] = edits; - const initialSelection = new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); - - model.pushEditOperations([initialSelection], edits.map(FormatOnSaveParticipant._asIdentEdit), undoEdits => { - for (const { range } of undoEdits) { - if (Range.areIntersectingOrTouching(range, initialSelection)) { - return [new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)]; - } - } - return null; - }); - } - - private static _asIdentEdit({ text, range }: TextEdit): IIdentifiedSingleEditOperation { - return { - text, - range: Range.lift(range), - forceMoveMarkers: true - }; - } } class CodeActionOnSaveParticipant implements ISaveParticipant { diff --git a/src/vs/workbench/api/browser/mainThreadSearch.ts b/src/vs/workbench/api/browser/mainThreadSearch.ts index 42b95d8729..83c3c4bf3c 100644 --- a/src/vs/workbench/api/browser/mainThreadSearch.ts +++ b/src/vs/workbench/api/browser/mainThreadSearch.ts @@ -44,7 +44,7 @@ export class MainThreadSearch implements MainThreadSearchShape { this._searchProvider.delete(handle); } - $handleFileMatch(handle: number, session, data: UriComponents[]): void { + $handleFileMatch(handle: number, session: number, data: UriComponents[]): void { const provider = this._searchProvider.get(handle); if (!provider) { throw new Error('Got result for unknown provider'); @@ -53,7 +53,7 @@ export class MainThreadSearch implements MainThreadSearchShape { provider.handleFindMatch(session, data); } - $handleTextMatch(handle: number, session, data: IRawFileMatch2[]): void { + $handleTextMatch(handle: number, session: number, data: IRawFileMatch2[]): void { const provider = this._searchProvider.get(handle); if (!provider) { throw new Error('Got result for unknown provider'); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e0c6f7f5f1..01799f5a0a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -335,8 +335,8 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerDocumentHighlightProvider(handle: number, selector: ISerializedDocumentFilter[]): void; $registerReferenceSupport(handle: number, selector: ISerializedDocumentFilter[]): void; $registerQuickFixSupport(handle: number, selector: ISerializedDocumentFilter[], supportedKinds?: string[]): void; - $registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier): void; - $registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier): void; + $registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier, displayName: string): void; + $registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier, displayName: string): void; $registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void; $registerNavigateTypeSupport(handle: number): void; $registerRenameSupport(handle: number, selector: ISerializedDocumentFilter[], supportsResolveInitialValues: boolean): void; @@ -511,27 +511,16 @@ export interface WebviewPanelShowOptions { readonly preserveFocus?: boolean; } -export interface IWebviewPanelOptions { - readonly enableFindWidget?: boolean; - readonly retainContextWhenHidden?: boolean; -} - -export interface IWebviewOptions { - readonly enableScripts?: boolean; - readonly enableCommandUris?: boolean; - readonly localResourceRoots?: ReadonlyArray; -} - export interface MainThreadWebviewsShape extends IDisposable { - $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: IWebviewPanelOptions & IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void; - $createWebviewCodeInset(handle: WebviewInsetHandle, symbolId: string, options: IWebviewOptions, extensionLocation: UriComponents | undefined): void; + $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void; + $createWebviewCodeInset(handle: WebviewInsetHandle, symbolId: string, options: modes.IWebviewOptions, extensionLocation: UriComponents | undefined): void; $disposeWebview(handle: WebviewPanelHandle): void; $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void; $setTitle(handle: WebviewPanelHandle, value: string): void; $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void; $setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void; - $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: IWebviewOptions): void; + $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: modes.IWebviewOptions): void; $postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, value: any): Promise; $registerSerializer(viewType: string): void; @@ -548,7 +537,7 @@ export interface ExtHostWebviewsShape { $onMessage(handle: WebviewPanelHandle, message: any): void; $onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, newState: WebviewPanelViewState): void; $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; - $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: IWebviewOptions): Promise; + $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions): Promise; } export interface MainThreadUrlsShape extends IDisposable { @@ -686,7 +675,7 @@ export interface MainThreadDebugServiceShape extends IDisposable { $acceptDAExit(handle: number, code: number | undefined, signal: string | undefined): void; $registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasProvideDaMethod: boolean, handle: number): Promise; $registerDebugAdapterDescriptorFactory(type: string, handle: number): Promise; - $registerDebugAdapterTrackerFactory(type: string, handle: number); + $registerDebugAdapterTrackerFactory(type: string, handle: number): Promise; $unregisterDebugConfigurationProvider(handle: number): void; $unregisterDebugAdapterDescriptorFactory(handle: number): void; $unregisterDebugAdapterTrackerFactory(handle: number): void; diff --git a/src/vs/workbench/api/electron-browser/mainThreadComments.ts b/src/vs/workbench/api/electron-browser/mainThreadComments.ts index 01f3c5b8f2..5e0c38d49b 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadComments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadComments.ts @@ -22,8 +22,9 @@ import { ICommentsConfiguration } from 'vs/workbench/contrib/comments/electron-b import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel'; -import { IRange } from 'vs/editor/common/core/range'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { Emitter, Event } from 'vs/base/common/event'; +import { CancellationToken } from 'vs/base/common/cancellation'; export class MainThreadDocumentCommentProvider implements modes.DocumentCommentProvider { private readonly _proxy: ExtHostCommentsShape; @@ -40,39 +41,39 @@ export class MainThreadDocumentCommentProvider implements modes.DocumentCommentP this._features = features; } - async provideDocumentComments(uri, token) { + async provideDocumentComments(uri: URI, token: CancellationToken) { return this._proxy.$provideDocumentComments(this._handle, uri); } - async createNewCommentThread(uri, range, text, token) { + async createNewCommentThread(uri: URI, range: Range, text: string, token: CancellationToken) { return this._proxy.$createNewCommentThread(this._handle, uri, range, text); } - async replyToCommentThread(uri, range, thread, text, token) { + async replyToCommentThread(uri: URI, range: Range, thread: modes.CommentThread, text: string, token: CancellationToken) { return this._proxy.$replyToCommentThread(this._handle, uri, range, thread, text); } - async editComment(uri, comment, text, token) { + async editComment(uri: URI, comment: modes.Comment, text: string, token: CancellationToken) { return this._proxy.$editComment(this._handle, uri, comment, text); } - async deleteComment(uri, comment, token) { + async deleteComment(uri: URI, comment: modes.Comment, token: CancellationToken) { return this._proxy.$deleteComment(this._handle, uri, comment); } - async startDraft(uri, token): Promise { + async startDraft(uri: URI, token: CancellationToken): Promise { return this._proxy.$startDraft(this._handle, uri); } - async deleteDraft(uri, token): Promise { + async deleteDraft(uri: URI, token: CancellationToken): Promise { return this._proxy.$deleteDraft(this._handle, uri); } - async finishDraft(uri, token): Promise { + async finishDraft(uri: URI, token: CancellationToken): Promise { return this._proxy.$finishDraft(this._handle, uri); } - async addReaction(uri, comment: modes.Comment, reaction: modes.CommentReaction, token): Promise { + async addReaction(uri: URI, comment: modes.Comment, reaction: modes.CommentReaction, token: CancellationToken): Promise { return this._proxy.$addReaction(this._handle, uri, comment, reaction); } - async deleteReaction(uri, comment: modes.Comment, reaction: modes.CommentReaction, token): Promise { + async deleteReaction(uri: URI, comment: modes.Comment, reaction: modes.CommentReaction, token: CancellationToken): Promise { return this._proxy.$deleteReaction(this._handle, uri, comment, reaction); } @@ -358,7 +359,7 @@ export class MainThreadCommentController { } - async getDocumentComments(resource: URI, token) { + async getDocumentComments(resource: URI, token: CancellationToken) { let ret: modes.CommentThread2[] = []; for (let thread of keys(this._threads)) { const commentThread = this._threads.get(thread)!; @@ -382,7 +383,7 @@ export class MainThreadCommentController { }; } - async getCommentingRanges(resource: URI, token): Promise { + async getCommentingRanges(resource: URI, token: CancellationToken): Promise { let commentingRanges = await this._proxy.$provideCommentingRanges(this.handle, resource, token); return commentingRanges || []; } @@ -391,7 +392,7 @@ export class MainThreadCommentController { return this._features.reactionGroup; } - async toggleReaction(uri, thread: modes.CommentThread2, comment: modes.Comment, reaction: modes.CommentReaction, token): Promise { + async toggleReaction(uri: URI, thread: modes.CommentThread2, comment: modes.Comment, reaction: modes.CommentReaction, token: CancellationToken): Promise { return this._proxy.$toggleReaction(this._handle, thread.commentThreadHandle, uri, comment, reaction); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadTask.ts b/src/vs/workbench/api/electron-browser/mainThreadTask.ts index bcf45b012c..67100bc848 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTask.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTask.ts @@ -17,15 +17,14 @@ import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspac import { ContributedTask, KeyedTaskIdentifier, TaskExecution, Task, TaskEvent, TaskEventKind, - PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource, TaskSourceKind, ExtensionTaskSource, RunOptions, TaskSet + PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource, + TaskSourceKind, ExtensionTaskSource, RunOptions, TaskSet, TaskDefinition } from 'vs/workbench/contrib/tasks/common/tasks'; import { ResolveSet, ResolvedVariables } from 'vs/workbench/contrib/tasks/common/taskSystem'; import { ITaskService, TaskFilter, ITaskProvider } from 'vs/workbench/contrib/tasks/common/taskService'; -import { TaskDefinition } from 'vs/workbench/contrib/tasks/node/tasks'; - import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index ac7d1360f2..9bc5f1dd00 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -13,7 +13,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewInsetHandle, WebviewPanelHandle, WebviewPanelShowOptions, IWebviewOptions } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewInsetHandle, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { CodeInsetController } from 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution'; import { WebviewEditor } from 'vs/workbench/contrib/webview/electron-browser/webviewEditor'; @@ -25,6 +25,7 @@ import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/commo import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { extHostNamedCustomer } from '../common/extHostCustomers'; +import { IWebviewOptions } from 'vs/editor/common/modes'; @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape { @@ -271,8 +272,8 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews private createWebviewEventDelegate(handle: WebviewPanelHandle) { return { - onDidClickLink: uri => this.onDidClickLink(handle, uri), - onMessage: message => this._proxy.$onMessage(handle, message), + onDidClickLink: (uri: URI) => this.onDidClickLink(handle, uri), + onMessage: (message: any) => this._proxy.$onMessage(handle, message), onDispose: () => { this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { this._webviews.delete(handle); @@ -391,7 +392,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews function reviveWebviewOptions(options: WebviewInputOptions): WebviewInputOptions { return { ...options, - localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(URI.revive) : undefined + localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(URI.revive) : undefined, }; } diff --git a/src/vs/workbench/api/node/apiCommands.ts b/src/vs/workbench/api/node/apiCommands.ts index dccc4dc132..f5cbe5fd47 100644 --- a/src/vs/workbench/api/node/apiCommands.ts +++ b/src/vs/workbench/api/node/apiCommands.ts @@ -48,10 +48,7 @@ export class OpenFolderAPICommand { if (!uri) { return executor.executeCommand('_files.pickFolderAndOpen', arg.forceNewWindow); } - const options: IOpenSettings = { forceNewWindow: arg.forceNewWindow }; - if (arg.noRecentEntry) { - options.args = { _: [], 'skip-add-to-recently-opened': true }; - } + const options: IOpenSettings = { forceNewWindow: arg.forceNewWindow, noRecentEntry: arg.noRecentEntry }; uri = URI.revive(uri); return executor.executeCommand('_files.windowOpen', [{ uri, label: arg.recentEntryLabel }], options); } @@ -74,7 +71,7 @@ interface INewWindowAPICommandOptions { export class NewWindowAPICommand { public static ID = 'vscode.newWindow'; public static execute(executor: ICommandsExecutor, options?: INewWindowAPICommandOptions): Promise { - return executor.executeCommand('_files.newWindow', [options]); + return executor.executeCommand('_files.newWindow', options); } } CommandsRegistry.registerCommand({ diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index e7a4b37e97..46bb57d30b 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -433,12 +433,15 @@ export function createApiFactory( onDidChangeWindowState(listener, thisArg?, disposables?) { return extHostWindow.onDidChangeWindowState(listener, thisArg, disposables); }, + // {{SQL CARBON EDIT}} Typing needs to be disabled; enabled strict null checks allows the typing once we get there showInformationMessage(message, first, ...rest) { return extHostMessageService.showMessage(extension, Severity.Info, message, first, rest); }, + // {{SQL CARBON EDIT}} Typing needs to be disabled; enabled strict null checks allows the typing once we get there showWarningMessage(message, first, ...rest) { return extHostMessageService.showMessage(extension, Severity.Warning, message, first, rest); }, + // {{SQL CARBON EDIT}} Typing needs to be disabled; enabled strict null checks allows the typing once we get there showErrorMessage(message, first, ...rest) { return extHostMessageService.showMessage(extension, Severity.Error, message, first, rest); }, @@ -540,17 +543,17 @@ export function createApiFactory( findFiles: (include, exclude, maxResults?, token?) => { return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(withNullAsUndefined(exclude)), maxResults, extension.identifier, token); }, - findTextInFiles: (query: vscode.TextSearchQuery, optionsOrCallback, callbackOrToken?, token?: vscode.CancellationToken) => { + findTextInFiles: (query: vscode.TextSearchQuery, optionsOrCallback: vscode.FindTextInFilesOptions | ((result: vscode.TextSearchResult) => void), callbackOrToken?: vscode.CancellationToken | ((result: vscode.TextSearchResult) => void), token?: vscode.CancellationToken) => { let options: vscode.FindTextInFilesOptions; let callback: (result: vscode.TextSearchResult) => void; if (typeof optionsOrCallback === 'object') { options = optionsOrCallback; - callback = callbackOrToken; + callback = callbackOrToken as (result: vscode.TextSearchResult) => void; } else { options = {}; callback = optionsOrCallback; - token = callbackOrToken; + token = callbackOrToken as vscode.CancellationToken; } return extHostWorkspace.findTextInFiles(query, options || {}, callback, extension.identifier, token); diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 7de4e6478d..453686d594 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -200,7 +200,7 @@ export class ExtHostApiCommands { description: 'Execute selection range provider.', args: [ { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'positions', description: 'Positions in a text document', constraint: a => Array.isArray(a) } + { name: 'positions', description: 'Positions in a text document', constraint: Array.isArray } ], returns: 'A promise that resolves to an array of ranges.' }); diff --git a/src/vs/workbench/api/node/extHostConfiguration.ts b/src/vs/workbench/api/node/extHostConfiguration.ts index b652271a3e..ec0bc0a589 100644 --- a/src/vs/workbench/api/node/extHostConfiguration.ts +++ b/src/vs/workbench/api/node/extHostConfiguration.ts @@ -125,7 +125,7 @@ export class ExtHostConfigProvider { if (typeof result === 'undefined') { result = defaultValue; } else { - let clonedConfig = undefined; + let clonedConfig: any | undefined = undefined; const cloneOnWriteProxy = (target: any, accessor: string): any => { let clonedTarget: any | undefined = undefined; const cloneTarget = () => { @@ -209,7 +209,7 @@ export class ExtHostConfigProvider { } private _toReadonlyValue(result: any): any { - const readonlyProxy = (target) => { + const readonlyProxy = (target: any): any => { return isObject(target) ? new Proxy(target, { get: (target: any, property: string) => readonlyProxy(target[property]), diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index c4c041b287..10ad9fe72c 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -1108,10 +1108,10 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return ExtHostLanguageFeatures._handlePool++; } - private _withAdapter(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A, extension: IExtensionDescription | undefined) => Promise): Promise { + private _withAdapter(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A, extension: IExtensionDescription | undefined) => Promise, fallbackValue: R): Promise { const data = this._adapter.get(handle); if (!data) { - return Promise.reject(new Error('no adapter found')); + return Promise.resolve(fallbackValue); } if (data.adapter instanceof ctor) { @@ -1156,7 +1156,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise { - return this._withAdapter(handle, DocumentSymbolAdapter, adapter => adapter.provideDocumentSymbols(URI.revive(resource), token)); + return this._withAdapter(handle, DocumentSymbolAdapter, adapter => adapter.provideDocumentSymbols(URI.revive(resource), token), undefined); } // --- code lens @@ -1178,11 +1178,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise { - return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token)); + return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), []); } $resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol, token: CancellationToken): Promise { - return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(URI.revive(resource), symbol, token)); + return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(URI.revive(resource), symbol, token), undefined); } // --- code insets @@ -1204,7 +1204,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideCodeInsets(handle: number, resource: UriComponents, token: CancellationToken): Promise { - return this._withAdapter(handle, CodeInsetAdapter, adapter => adapter.provideCodeInsets(URI.revive(resource), token)); + return this._withAdapter(handle, CodeInsetAdapter, adapter => adapter.provideCodeInsets(URI.revive(resource), token), undefined); } $resolveCodeInset(handle: number, _resource: UriComponents, symbol: codeInset.ICodeInsetSymbol, token: CancellationToken): Promise { @@ -1213,7 +1213,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, CodeInsetAdapter, async (adapter, extension) => { await this._webviewProxy.$createWebviewCodeInset(webviewHandle, symbol.id, { enableCommandUris: true, enableScripts: true }, extension ? extension.extensionLocation : undefined); return adapter.resolveCodeInset(symbol, webview, token); - }); + }, symbol); } // --- declaration @@ -1225,7 +1225,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { - return this._withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position, token)); + return this._withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position, token), []); } registerDeclarationProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DeclarationProvider): vscode.Disposable { @@ -1235,7 +1235,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { - return this._withAdapter(handle, DeclarationAdapter, adapter => adapter.provideDeclaration(URI.revive(resource), position, token)); + return this._withAdapter(handle, DeclarationAdapter, adapter => adapter.provideDeclaration(URI.revive(resource), position, token), []); } registerImplementationProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable { @@ -1245,7 +1245,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { - return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position, token)); + return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position, token), []); } registerTypeDefinitionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable { @@ -1255,7 +1255,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { - return this._withAdapter(handle, TypeDefinitionAdapter, adapter => adapter.provideTypeDefinition(URI.revive(resource), position, token)); + return this._withAdapter(handle, TypeDefinitionAdapter, adapter => adapter.provideTypeDefinition(URI.revive(resource), position, token), []); } // --- extra info @@ -1267,7 +1267,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { - return this._withAdapter(handle, HoverAdapter, adapter => adapter.provideHover(URI.revive(resource), position, token)); + return this._withAdapter(handle, HoverAdapter, adapter => adapter.provideHover(URI.revive(resource), position, token), undefined); } // --- occurrences @@ -1279,7 +1279,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { - return this._withAdapter(handle, DocumentHighlightAdapter, adapter => adapter.provideDocumentHighlights(URI.revive(resource), position, token)); + return this._withAdapter(handle, DocumentHighlightAdapter, adapter => adapter.provideDocumentHighlights(URI.revive(resource), position, token), undefined); } // --- references @@ -1291,7 +1291,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise { - return this._withAdapter(handle, ReferenceAdapter, adapter => adapter.provideReferences(URI.revive(resource), position, context, token)); + return this._withAdapter(handle, ReferenceAdapter, adapter => adapter.provideReferences(URI.revive(resource), position, context, token), undefined); } // --- quick fix @@ -1304,29 +1304,29 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { $provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise { - return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), rangeOrSelection, context, token)); + return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), rangeOrSelection, context, token), undefined); } // --- formatting registerDocumentFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable { const handle = this._addNewAdapter(new DocumentFormattingAdapter(this._documents, provider), extension); - this._proxy.$registerDocumentFormattingSupport(handle, this._transformDocumentSelector(selector), extension.identifier); + this._proxy.$registerDocumentFormattingSupport(handle, this._transformDocumentSelector(selector), extension.identifier, extension.displayName || extension.name); return this._createDisposable(handle); } $provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions, token: CancellationToken): Promise { - return this._withAdapter(handle, DocumentFormattingAdapter, adapter => adapter.provideDocumentFormattingEdits(URI.revive(resource), options, token)); + return this._withAdapter(handle, DocumentFormattingAdapter, adapter => adapter.provideDocumentFormattingEdits(URI.revive(resource), options, token), undefined); } registerDocumentRangeFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable { const handle = this._addNewAdapter(new RangeFormattingAdapter(this._documents, provider), extension); - this._proxy.$registerRangeFormattingSupport(handle, this._transformDocumentSelector(selector), extension.identifier); + this._proxy.$registerRangeFormattingSupport(handle, this._transformDocumentSelector(selector), extension.identifier, extension.displayName || extension.name); return this._createDisposable(handle); } $provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions, token: CancellationToken): Promise { - return this._withAdapter(handle, RangeFormattingAdapter, adapter => adapter.provideDocumentRangeFormattingEdits(URI.revive(resource), range, options, token)); + return this._withAdapter(handle, RangeFormattingAdapter, adapter => adapter.provideDocumentRangeFormattingEdits(URI.revive(resource), range, options, token), undefined); } registerOnTypeFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, triggerCharacters: string[]): vscode.Disposable { @@ -1336,7 +1336,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Promise { - return this._withAdapter(handle, OnTypeFormattingAdapter, adapter => adapter.provideOnTypeFormattingEdits(URI.revive(resource), position, ch, options, token)); + return this._withAdapter(handle, OnTypeFormattingAdapter, adapter => adapter.provideOnTypeFormattingEdits(URI.revive(resource), position, ch, options, token), undefined); } // --- navigate types @@ -1348,15 +1348,15 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Promise { - return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.provideWorkspaceSymbols(search, token)); + return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.provideWorkspaceSymbols(search, token), { symbols: [] }); } $resolveWorkspaceSymbol(handle: number, symbol: WorkspaceSymbolDto, token: CancellationToken): Promise { - return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.resolveWorkspaceSymbol(symbol, token)); + return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.resolveWorkspaceSymbol(symbol, token), undefined); } $releaseWorkspaceSymbols(handle: number, id: number): void { - this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.releaseWorkspaceSymbols(id)); + this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.releaseWorkspaceSymbols(id), undefined); } // --- rename @@ -1368,11 +1368,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise { - return this._withAdapter(handle, RenameAdapter, adapter => adapter.provideRenameEdits(URI.revive(resource), position, newName, token)); + return this._withAdapter(handle, RenameAdapter, adapter => adapter.provideRenameEdits(URI.revive(resource), position, newName, token), undefined); } $resolveRenameLocation(handle: number, resource: URI, position: IPosition, token: CancellationToken): Promise { - return this._withAdapter(handle, RenameAdapter, adapter => adapter.resolveRenameLocation(URI.revive(resource), position, token)); + return this._withAdapter(handle, RenameAdapter, adapter => adapter.resolveRenameLocation(URI.revive(resource), position, token), undefined); } // --- suggestion @@ -1384,15 +1384,15 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise { - return this._withAdapter(handle, SuggestAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token)); + return this._withAdapter(handle, SuggestAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token), undefined); } $resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.CompletionItem, token: CancellationToken): Promise { - return this._withAdapter(handle, SuggestAdapter, adapter => adapter.resolveCompletionItem(URI.revive(resource), position, suggestion, token)); + return this._withAdapter(handle, SuggestAdapter, adapter => adapter.resolveCompletionItem(URI.revive(resource), position, suggestion, token), suggestion); } $releaseCompletionItems(handle: number, id: number): void { - this._withAdapter(handle, SuggestAdapter, adapter => adapter.releaseCompletionItems(id)); + this._withAdapter(handle, SuggestAdapter, adapter => adapter.releaseCompletionItems(id), undefined); } // --- parameter hints @@ -1408,7 +1408,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise { - return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, context, token)); + return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, context, token), undefined); } // --- links @@ -1420,11 +1420,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise { - return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.provideLinks(URI.revive(resource), token)); + return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.provideLinks(URI.revive(resource), token), undefined); } $resolveDocumentLink(handle: number, link: modes.ILink, token: CancellationToken): Promise { - return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(link, token)); + return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(link, token), undefined); } registerColorProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { @@ -1434,11 +1434,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideDocumentColors(handle: number, resource: UriComponents, token: CancellationToken): Promise { - return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColors(URI.revive(resource), token)); + return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColors(URI.revive(resource), token), []); } $provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo, token: CancellationToken): Promise { - return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token)); + return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token), undefined); } registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { @@ -1448,7 +1448,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideFoldingRanges(handle: number, resource: UriComponents, context: vscode.FoldingContext, token: CancellationToken): Promise { - return this._withAdapter(handle, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource), context, token)); + return this._withAdapter(handle, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource), context, token), undefined); } // --- smart select @@ -1460,7 +1460,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideSelectionRanges(handle: number, resource: UriComponents, positions: IPosition[], token: CancellationToken): Promise { - return this._withAdapter(handle, SelectionRangeAdapter, adapter => adapter.provideSelectionRanges(URI.revive(resource), positions, token)); + return this._withAdapter(handle, SelectionRangeAdapter, adapter => adapter.provideSelectionRanges(URI.revive(resource), positions, token), []); } // --- call hierarchy @@ -1472,11 +1472,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } $provideCallHierarchyItem(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { - return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallHierarchyItem(URI.revive(resource), position, token)); + return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallHierarchyItem(URI.revive(resource), position, token), undefined); } $resolveCallHierarchyItem(handle: number, item: callHierarchy.CallHierarchyItem, direction: callHierarchy.CallHierarchyDirection, token: CancellationToken): Promise<[callHierarchy.CallHierarchyItem, modes.Location[]][]> { - return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.resolveCallHierarchyItem(item, direction, token)); + return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.resolveCallHierarchyItem(item, direction, token), []); } // --- configuration diff --git a/src/vs/workbench/api/node/extHostMessageService.ts b/src/vs/workbench/api/node/extHostMessageService.ts index 493bd297a5..603132e65e 100644 --- a/src/vs/workbench/api/node/extHostMessageService.ts +++ b/src/vs/workbench/api/node/extHostMessageService.ts @@ -22,7 +22,8 @@ export class ExtHostMessageService { showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string, rest: string[]): Promise; showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem, rest: vscode.MessageItem[]): Promise; - showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): Promise { + showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem | string, rest: Array): Promise; + showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: Array): Promise { const options: MainThreadMessageOptions = { extension }; let items: (string | vscode.MessageItem)[]; diff --git a/src/vs/workbench/api/node/extHostQuickOpen.ts b/src/vs/workbench/api/node/extHostQuickOpen.ts index 518a760ede..4e3ca68410 100644 --- a/src/vs/workbench/api/node/extHostQuickOpen.ts +++ b/src/vs/workbench/api/node/extHostQuickOpen.ts @@ -175,13 +175,13 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { // ---- QuickInput createQuickPick(extensionId: ExtensionIdentifier, enableProposedApi: boolean): QuickPick { - const session = new ExtHostQuickPick(this._proxy, extensionId, enableProposedApi, () => this._sessions.delete(session._id)); + const session: ExtHostQuickPick = new ExtHostQuickPick(this._proxy, extensionId, enableProposedApi, () => this._sessions.delete(session._id)); this._sessions.set(session._id, session); return session; } createInputBox(extensionId: ExtensionIdentifier): InputBox { - const session = new ExtHostInputBox(this._proxy, extensionId, () => this._sessions.delete(session._id)); + const session: ExtHostInputBox = new ExtHostInputBox(this._proxy, extensionId, () => this._sessions.delete(session._id)); this._sessions.set(session._id, session); return session; } @@ -379,7 +379,7 @@ class ExtHostQuickInput implements QuickInput { this._onDidAcceptEmitter.fire(); } - _fireDidChangeValue(value) { + _fireDidChangeValue(value: string) { this._value = value; this._onDidChangeValueEmitter.fire(value); } diff --git a/src/vs/workbench/api/node/extHostTreeViews.ts b/src/vs/workbench/api/node/extHostTreeViews.ts index 512a74ec7e..ba25c29f63 100644 --- a/src/vs/workbench/api/node/extHostTreeViews.ts +++ b/src/vs/workbench/api/node/extHostTreeViews.ts @@ -195,7 +195,8 @@ export class ExtHostTreeView extends Disposable { this._register(this.dataProvider.onDidChangeTreeData(element => this._onDidChangeData.fire({ message: false, element }))); } - let refreshingPromise, promiseCallback; + let refreshingPromise: Promise | null; + let promiseCallback: () => void; this._register(Event.debounce, { message: boolean, elements: (T | Root)[] }>(this._onDidChangeData.event, (result, current) => { if (!result) { result = { message: false, elements: [] }; @@ -204,7 +205,7 @@ export class ExtHostTreeView extends Disposable { if (!refreshingPromise) { // New refresh has started refreshingPromise = new Promise(c => promiseCallback = c); - this.refreshPromise = this.refreshPromise.then(() => refreshingPromise); + this.refreshPromise = this.refreshPromise.then(() => refreshingPromise!); } result.elements.push(current.element); } diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index ad2f1322cc..1bfafb8109 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1058,15 +1058,15 @@ export class CodeAction { export class CodeActionKind { private static readonly sep = '.'; - public static Empty; - public static QuickFix; - public static Refactor; - public static RefactorExtract; - public static RefactorInline; - public static RefactorRewrite; - public static Source; - public static SourceOrganizeImports; - public static SourceFixAll; + public static Empty: CodeActionKind; + public static QuickFix: CodeActionKind; + public static Refactor: CodeActionKind; + public static RefactorExtract: CodeActionKind; + public static RefactorInline: CodeActionKind; + public static RefactorRewrite: CodeActionKind; + public static Source: CodeActionKind; + public static SourceOrganizeImports: CodeActionKind; + public static SourceFixAll: CodeActionKind; constructor( public readonly value: string diff --git a/src/vs/workbench/browser/actions.ts b/src/vs/workbench/browser/actions.ts index d9875d539b..c8600c33b4 100644 --- a/src/vs/workbench/browser/actions.ts +++ b/src/vs/workbench/browser/actions.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { Registry } from 'vs/platform/registry/common/platform'; -import { Action, IAction } from 'vs/base/common/actions'; -import { BaseActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction } from 'vs/base/common/actions'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree'; import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -27,27 +27,6 @@ export class ActionBarContributor { getActions(context: any): IAction[] { return []; } - - /** - * Returns true if this contributor has secondary actions for the given context. - */ - hasSecondaryActions(context: any): boolean { - return false; - } - - /** - * Returns an array of secondary actions in the given context. - */ - getSecondaryActions(context: any): IAction[] { - return []; - } - - /** - * Can return a specific IActionItem to render the given action. - */ - getActionItem(context: any, action: Action): BaseActionItem | null { - return null; - } } /** @@ -101,50 +80,6 @@ export class ContributableActionProvider implements IActionProvider { return prepareActions(actions); } - - hasSecondaryActions(tree: ITree, element: any): boolean { - const context = this.toContext(tree, element); - - const contributors = this.registry.getActionBarContributors(Scope.VIEWER); - for (const contributor of contributors) { - if (contributor.hasSecondaryActions(context)) { - return true; - } - } - - return false; - } - - getSecondaryActions(tree: ITree, element: any): IAction[] { - const actions: IAction[] = []; - const context = this.toContext(tree, element); - - // Collect Actions - const contributors = this.registry.getActionBarContributors(Scope.VIEWER); - for (const contributor of contributors) { - if (contributor.hasSecondaryActions(context)) { - actions.push(...contributor.getSecondaryActions(context)); - } - } - - return prepareActions(actions); - } - - getActionItem(tree: ITree, element: any, action: Action): BaseActionItem | null { - const contributors = this.registry.getActionBarContributors(Scope.VIEWER); - const context = this.toContext(tree, element); - - for (let i = contributors.length - 1; i >= 0; i--) { - const contributor = contributors[i]; - - const itemProvider = contributor.getActionItem(context, action); - if (itemProvider) { - return itemProvider; - } - } - - return null; - } } // Helper function used in parts to massage actions before showing in action areas @@ -201,25 +136,6 @@ export const Extensions = { }; export interface IActionBarRegistry { - - /** - * Goes through all action bar contributors and asks them for contributed actions for - * the provided scope and context. Supports primary actions. - */ - getActionBarActionsForContext(scope: string, context: any): IAction[]; - - /** - * Goes through all action bar contributors and asks them for contributed actions for - * the provided scope and context. Supports secondary actions. - */ - getSecondaryActionBarActionsForContext(scope: string, context: any): IAction[]; - - /** - * Goes through all action bar contributors and asks them for contributed action item for - * the provided scope and context. - */ - getActionItemForContext(scope: string, context: any, action: Action): BaseActionItem | null; - /** * Registers an Actionbar contributor. It will be called to contribute actions to all the action bars * that are used in the Workbench in the given scope. @@ -264,48 +180,6 @@ class ActionBarRegistry implements IActionBarRegistry { return this.actionBarContributorInstances[scope] || []; } - getActionBarActionsForContext(scope: string, context: any): IAction[] { - const actions: IAction[] = []; - - // Go through contributors for scope - this.getContributors(scope).forEach((contributor: ActionBarContributor) => { - - // Primary Actions - if (contributor.hasActions(context)) { - actions.push(...contributor.getActions(context)); - } - }); - - return actions; - } - - getSecondaryActionBarActionsForContext(scope: string, context: any): IAction[] { - const actions: IAction[] = []; - - // Go through contributors - this.getContributors(scope).forEach((contributor: ActionBarContributor) => { - - // Secondary Actions - if (contributor.hasSecondaryActions(context)) { - actions.push(...contributor.getSecondaryActions(context)); - } - }); - - return actions; - } - - getActionItemForContext(scope: string, context: any, action: Action): BaseActionItem | null { - const contributors = this.getContributors(scope); - for (const contributor of contributors) { - const item = contributor.getActionItem(context, action); - if (item) { - return item; - } - } - - return null; - } - registerActionBarContributor(scope: string, ctor: IConstructorSignature0): void { if (!this.instantiationService) { this.actionBarContributorConstructors.push({ diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index 23d55d4291..58a1a0e9d1 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -166,11 +166,11 @@ export abstract class Composite extends Component implements IComposite { /** * For any of the actions returned by this composite, provide an IActionItem in * cases where the implementor of the composite wants to override the presentation - * of an action. Returns null to indicate that the action is not rendered through + * of an action. Returns undefined to indicate that the action is not rendered through * an action item. */ - getActionItem(action: IAction): IActionItem | null { - return null; + getActionItem(action: IAction): IActionItem | undefined { + return undefined; } /** diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 85c3589038..a5cfa4777f 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; @@ -76,8 +76,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private _container: HTMLElement = document.createElement('div'); get container(): HTMLElement { return this._container; } - get hasWorkbench(): boolean { return true; } - private parts: Map = new Map(); private workbenchGrid: Grid | WorkbenchLegacyLayout; diff --git a/src/vs/workbench/browser/nodeless.simpleservices.ts b/src/vs/workbench/browser/nodeless.simpleservices.ts index 4bcde0ffba..8242be5744 100644 --- a/src/vs/workbench/browser/nodeless.simpleservices.ts +++ b/src/vs/workbench/browser/nodeless.simpleservices.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { ITextSnapshot, IFileStat, IContent, IFileService, IResourceEncodings, IResolveFileOptions, IResolveFileResult, IResolveContentOptions, IStreamContent, IUpdateContentOptions, snapshotToString, ICreateFileOptions, IResourceEncoding } from 'vs/platform/files/common/files'; +import { ITextSnapshot, IFileStat, IContent, IFileService, IResourceEncodings, IResolveFileOptions, IResolveFileResult, IResolveContentOptions, IStreamContent, IUpdateContentOptions, snapshotToString, ICreateFileOptions, IResourceEncoding, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { ITextBufferFactory } from 'vs/editor/common/model'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { keys, ResourceMap } from 'vs/base/common/map'; @@ -60,6 +60,8 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/resour import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Color, RGBA } from 'vs/base/common/color'; +import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { IRemoteAgentService, IRemoteAgentConnection } from 'vs/workbench/services/remote/common/remoteAgentService'; export const workspaceResource = URI.file(isWindows ? 'C:\\simpleWorkspace' : '/simpleWorkspace'); @@ -640,26 +642,19 @@ registerSingleton(IProductService, SimpleProductService, true); //#region Remote Agent -export const IRemoteAgentService = createDecorator('remoteAgentService'); - -export interface IRemoteAgentService { - _serviceBrand: any; - - getConnection(): object; -} - export class SimpleRemoteAgentService implements IRemoteAgentService { _serviceBrand: any; - getConnection(): object { - // @ts-ignore - return undefined; + getConnection(): IRemoteAgentConnection | null { + return null; + } + + getEnvironment(): Promise { + return Promise.resolve(null); } } -registerSingleton(IRemoteAgentService, SimpleRemoteAgentService); - //#endregion //#region Remote Authority Resolver @@ -699,7 +694,7 @@ export class SimpleRemoteFileService implements IFileService { readonly onDidChangeFileSystemProviderRegistrations = Event.None; readonly onWillActivateFileSystemProvider = Event.None; - resolveFile(resource: URI, options?: IResolveFileOptions): Promise { + resolveFile(resource: URI, options?: IResolveFileOptions): Promise { // @ts-ignore return Promise.resolve(fileMap.get(resource)); } @@ -741,12 +736,14 @@ export class SimpleRemoteFileService implements IFileService { // @ts-ignore mtime: content.mtime, // @ts-ignore - name: content.name + name: content.name, + // @ts-ignore + size: content.size }; }); } - updateContent(resource: URI, value: string | ITextSnapshot, _options?: IUpdateContentOptions): Promise { + updateContent(resource: URI, value: string | ITextSnapshot, _options?: IUpdateContentOptions): Promise { // @ts-ignore return Promise.resolve(fileMap.get(resource)).then(file => { const content = contentMap.get(resource); @@ -763,7 +760,7 @@ export class SimpleRemoteFileService implements IFileService { }); } - moveFile(_source: URI, _target: URI, _overwrite?: boolean): Promise { return Promise.resolve(null!); } + moveFile(_source: URI, _target: URI, _overwrite?: boolean): Promise { return Promise.resolve(null!); } copyFile(_source: URI, _target: URI, _overwrite?: boolean): Promise { const parent = fileMap.get(dirname(_target)); @@ -776,7 +773,7 @@ export class SimpleRemoteFileService implements IFileService { }); } - createFile(_resource: URI, _content?: string, _options?: ICreateFileOptions): Promise { + createFile(_resource: URI, _content?: string, _options?: ICreateFileOptions): Promise { const parent = fileMap.get(dirname(_resource)); if (!parent) { return Promise.reject(new Error(`Unable to create file in ${dirname(_resource).path}`)); @@ -785,7 +782,7 @@ export class SimpleRemoteFileService implements IFileService { return Promise.resolve(createFile(parent, basename(_resource.path))); } - createFolder(_resource: URI): Promise { + createFolder(_resource: URI): Promise { const parent = fileMap.get(dirname(_resource)); if (!parent) { return Promise.reject(new Error(`Unable to create folder in ${dirname(_resource).path}`)); @@ -794,7 +791,7 @@ export class SimpleRemoteFileService implements IFileService { return Promise.resolve(createFolder(parent, basename(_resource.path))); } - registerProvider(_scheme: string, _provider) { return { dispose() { } }; } + registerProvider() { return { dispose() { } }; } activateProvider(_scheme: string): Promise { return Promise.resolve(undefined); } @@ -811,13 +808,14 @@ export class SimpleRemoteFileService implements IFileService { dispose(): void { } } -function createFile(parent: IFileStat, name: string, content: string = ''): IFileStat { - const file: IFileStat = { +function createFile(parent: IFileStat, name: string, content: string = ''): IFileStatWithMetadata { + const file: IFileStatWithMetadata = { resource: joinPath(parent.resource, name), etag: Date.now().toString(), mtime: Date.now(), isDirectory: false, - name + name, + size: -1 }; // @ts-ignore @@ -837,13 +835,14 @@ function createFile(parent: IFileStat, name: string, content: string = ''): IFil return file; } -function createFolder(parent: IFileStat, name: string): IFileStat { - const folder: IFileStat = { +function createFolder(parent: IFileStat, name: string): IFileStatWithMetadata { + const folder: IFileStatWithMetadata = { resource: joinPath(parent.resource, name), etag: Date.now().toString(), mtime: Date.now(), isDirectory: true, name, + size: 0, children: [] }; @@ -863,7 +862,8 @@ function initFakeFileSystem(): void { mtime: Date.now(), isDirectory: true, name: basename(workspaceResource.fsPath), - children: [] + children: [], + size: 0 }; fileMap.set(root.resource, root); @@ -1059,14 +1059,14 @@ export const IRequestService = createDecorator('requestService' export interface IRequestService { _serviceBrand: any; - request(options, token: CancellationToken): Promise; + request(options: any, token: CancellationToken): Promise; } export class SimpleRequestService implements IRequestService { _serviceBrand: any; - request(options, token: CancellationToken): Promise { + request(options: any, token: CancellationToken): Promise { return Promise.resolve(Object.create(null)); } } diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index e278205701..fb24f033bf 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -50,7 +50,7 @@ export class CompositeBar extends Widget implements ICompositeBar { private compositeSwitcherBar: ActionBar; private compositeOverflowAction: CompositeOverflowActivityAction | null; - private compositeOverflowActionItem: CompositeOverflowActivityActionItem | null; + private compositeOverflowActionItem: CompositeOverflowActivityActionItem | undefined; private model: CompositeBarModel; private visibleComposites: string[]; @@ -345,7 +345,7 @@ export class CompositeBar extends Widget implements ICompositeBar { if (this.compositeOverflowActionItem) { this.compositeOverflowActionItem.dispose(); } - this.compositeOverflowActionItem = null; + this.compositeOverflowActionItem = undefined; } // Pull out composites that overflow or got hidden diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 389d4e87bb..7e9c88c371 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -432,14 +432,14 @@ export abstract class CompositePart extends Part { this.titleLabel.updateStyles(); } - protected actionItemProvider(action: Action): IActionItem | null { + protected actionItemProvider(action: Action): IActionItem | undefined { // Check Active Composite if (this.activeComposite) { return this.activeComposite.getActionItem(action); } - return null; + return undefined; } protected actionsContextProvider(): any { diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index ded8b64f08..a9c6fd05a0 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -114,9 +114,9 @@ class UntitledEditorInputFactory implements IEditorInputFactory { @ITextFileService private readonly textFileService: ITextFileService ) { } - serialize(editorInput: EditorInput): string | null { + serialize(editorInput: EditorInput): string | undefined { if (!this.textFileService.isHotExitEnabled) { - return null; // never restore untitled unless hot exit is enabled + return undefined; // never restore untitled unless hot exit is enabled } const untitledEditorInput = editorInput; @@ -170,7 +170,7 @@ interface ISerializedSideBySideEditorInput { // Register Side by Side Editor Input Factory class SideBySideEditorInputFactory implements IEditorInputFactory { - serialize(editorInput: EditorInput): string | null { + serialize(editorInput: EditorInput): string | undefined { const input = editorInput; if (input.details && input.master) { @@ -195,10 +195,10 @@ class SideBySideEditorInputFactory implements IEditorInputFactory { } } - return null; + return undefined; } - deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | null { + deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | undefined { const deserialized: ISerializedSideBySideEditorInput = JSON.parse(serializedEditorInput); const registry = Registry.as(EditorInputExtensions.EditorInputFactories); @@ -214,7 +214,7 @@ class SideBySideEditorInputFactory implements IEditorInputFactory { } } - return null; + return undefined; } } diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index c9274c1253..c71e8fcc5f 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -39,7 +39,7 @@ import { Themable } from 'vs/workbench/common/theme'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IFileService } from 'vs/platform/files/common/files'; -import { withUndefinedAsNull } from 'vs/base/common/types'; +import { withNullAsUndefined } from 'vs/base/common/types'; export interface IToolbarActions { primary: IAction[]; @@ -156,18 +156,18 @@ export abstract class TitleControl extends Themable { })); } - private actionItemProvider(action: Action): IActionItem | null { + private actionItemProvider(action: Action): IActionItem | undefined { const activeControl = this.group.activeControl; // Check Active Editor - let actionItem: IActionItem | null = null; + let actionItem: IActionItem | undefined = undefined; if (activeControl instanceof BaseEditor) { actionItem = activeControl.getActionItem(action); } // Check extensions if (!actionItem) { - actionItem = withUndefinedAsNull(createActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService)); + actionItem = createActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); } return actionItem; @@ -323,7 +323,7 @@ export abstract class TitleControl extends Themable { protected getKeybindingLabel(action: IAction): string | undefined { const keybinding = this.getKeybinding(action); - return keybinding ? keybinding.getLabel() || undefined : undefined; + return keybinding ? withNullAsUndefined(keybinding.getLabel()) : undefined; } abstract openEditor(editor: IEditorInput): void; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index 1fc8a800a4..8706689698 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -227,7 +227,7 @@ export class NotificationRenderer implements IListRenderer implements IPanelService { } } - return this.openComposite(id, focus) || null; + return withUndefinedAsNull(this.openComposite(id, focus)); } showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index dd625d4fdb..0de179e7d4 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -309,7 +309,7 @@ class QuickPick extends QuickInput implements IQuickPi private static INPUT_BOX_ARIA_LABEL = localize('quickInputBox.ariaLabel', "Type to narrow down results."); private _value = ''; - private _placeholder; + private _placeholder: string; private onDidChangeValueEmitter = new Emitter(); private onDidAcceptEmitter = new Emitter(); private _items: Array = []; @@ -499,6 +499,7 @@ class QuickPick extends QuickInput implements IQuickPi if (this.canSelectMany) { this.ui.list.domFocus(); } + event.preventDefault(); break; case KeyCode.UpArrow: if (this.ui.list.getFocusedElements().length) { @@ -509,6 +510,7 @@ class QuickPick extends QuickInput implements IQuickPi if (this.canSelectMany) { this.ui.list.domFocus(); } + event.preventDefault(); break; case KeyCode.PageDown: if (this.ui.list.getFocusedElements().length) { @@ -519,6 +521,7 @@ class QuickPick extends QuickInput implements IQuickPi if (this.canSelectMany) { this.ui.list.domFocus(); } + event.preventDefault(); break; case KeyCode.PageUp: if (this.ui.list.getFocusedElements().length) { @@ -529,6 +532,7 @@ class QuickPick extends QuickInput implements IQuickPi if (this.canSelectMany) { this.ui.list.domFocus(); } + event.preventDefault(); break; } }), diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts index cf997f89cf..3184f073df 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts @@ -28,6 +28,7 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action } from 'vs/base/common/actions'; import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils'; import { IListOptions } from 'vs/base/browser/ui/list/listWidget'; +import { withNullAsUndefined } from 'vs/base/common/types'; const $ = dom.$; @@ -492,9 +493,9 @@ export class QuickInputList { // Filter by value (since we support octicons, use octicon aware fuzzy matching) else { this.elements.forEach(element => { - const labelHighlights = this.matchOnLabel ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneLabel)) || undefined : undefined; - const descriptionHighlights = this.matchOnDescription ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneDescription || '')) || undefined : undefined; - const detailHighlights = this.matchOnDetail ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneDetail || '')) || undefined : undefined; + const labelHighlights = this.matchOnLabel ? withNullAsUndefined(matchesFuzzyOcticonAware(query, parseOcticons(element.saneLabel))) : undefined; + const descriptionHighlights = this.matchOnDescription ? withNullAsUndefined(matchesFuzzyOcticonAware(query, parseOcticons(element.saneDescription || ''))) : undefined; + const detailHighlights = this.matchOnDetail ? withNullAsUndefined(matchesFuzzyOcticonAware(query, parseOcticons(element.saneDetail || ''))) : undefined; if (labelHighlights || descriptionHighlights || detailHighlights) { element.labelHighlights = labelHighlights; diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index be7b009326..7da7903fab 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -783,7 +783,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry { } getResource(): URI | null { - return this.resource || null; + return types.withUndefinedAsNull(this.resource); } getInput(): IEditorInput | IResourceInput { @@ -848,7 +848,7 @@ export class RemoveFromEditorHistoryAction extends Action { return { input: h, - iconClasses: getIconClasses(this.modelService, this.modeService, entry.getResource() || undefined), + iconClasses: getIconClasses(this.modelService, this.modeService, types.withNullAsUndefined(entry.getResource())), label: entry.getLabel(), description: entry.getDescription() }; diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 0bd89617cc..064aff3a4d 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -87,8 +87,8 @@ export class CustomTreeViewPanel extends ViewletPanel { return [...this.treeView.getSecondaryActions()]; } - getActionItem(action: IAction): IActionItem | null { - return action instanceof MenuItemAction ? new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService) : null; + getActionItem(action: IAction): IActionItem | undefined { + return action instanceof MenuItemAction ? new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService) : undefined; } getOptimalWidth(): number { @@ -378,7 +378,7 @@ export class CustomTreeView extends Disposable implements ITreeView { } private createTree() { - const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : null; + const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : undefined; const menus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); const dataSource = this.instantiationService.createInstance(TreeDataSource, this, (task: Promise) => this.progressService.withProgress({ location: this.viewContainer.id }, () => task)); @@ -814,7 +814,7 @@ class TreeController extends WorkbenchTreeController { if (keybinding) { return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() }); } - return null; + return undefined; }, onHide: (wasCancelled?: boolean) => { diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index 25ad73e201..9710103443 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -174,8 +174,8 @@ export abstract class ViewletPanel extends Panel implements IView { return []; } - getActionItem(action: IAction): IActionItem | null { - return null; + getActionItem(action: IAction): IActionItem | undefined { + return undefined; } getActionsContext(): any { @@ -282,7 +282,7 @@ export class PanelViewlet extends Viewlet { return []; } - getActionItem(action: IAction): IActionItem | null { + getActionItem(action: IAction): IActionItem | undefined { if (this.isSingleView()) { return this.panelItems[0].panel.getActionItem(action); } diff --git a/src/vs/workbench/browser/quickopen.ts b/src/vs/workbench/browser/quickopen.ts index 429e0e6138..dc841b4604 100644 --- a/src/vs/workbench/browser/quickopen.ts +++ b/src/vs/workbench/browser/quickopen.ts @@ -214,7 +214,7 @@ class QuickOpenRegistry implements IQuickOpenRegistry { } getQuickOpenHandler(text: string): QuickOpenHandlerDescriptor | null { - return text ? arrays.first(this.handlers, h => strings.startsWith(text, h.prefix), null) : null; + return text ? (arrays.first(this.handlers, h => strings.startsWith(text, h.prefix)) || null) : null; } getDefaultQuickOpenHandler(): QuickOpenHandlerDescriptor { @@ -280,7 +280,7 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick opts = EditorOptions.create(openOptions); } - this.editorService.openEditor(input, opts || undefined, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + this.editorService.openEditor(input, types.withNullAsUndefined(opts), sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } else { const resourceInput = input; diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 1350a20bd6..c1a80a95b6 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -82,7 +82,7 @@ export class Workbench extends Layout { // Inform user about loading issues from the loader (window).require.config({ - onError: err => { + onError: (err: { errorCode: string; }) => { if (err.errorCode === 'load') { onUnexpectedError(new Error(localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err)))); } diff --git a/src/vs/workbench/common/actions.ts b/src/vs/workbench/common/actions.ts index 734678a6dd..dc2cabc3b1 100644 --- a/src/vs/workbench/common/actions.ts +++ b/src/vs/workbench/common/actions.ts @@ -6,7 +6,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId, ICommandAction } from 'vs/platform/actions/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -58,16 +58,16 @@ Registry.add(Extensions.WorkbenchActions, new class implements IWorkbenchActionR if (descriptor.label) { let idx = alias.indexOf(': '); - let categoryOriginal; + let categoryOriginal = ''; if (idx > 0) { categoryOriginal = alias.substr(0, idx); alias = alias.substr(idx + 2); } - const command = { + const command: ICommandAction = { id: descriptor.id, title: { value: descriptor.label, original: alias }, - category: category && { value: category, original: categoryOriginal } + category: category ? { value: category, original: categoryOriginal } : undefined }; MenuRegistry.addCommand(command); diff --git a/src/vs/workbench/common/composite.ts b/src/vs/workbench/common/composite.ts index 22a038525c..692188a4ca 100644 --- a/src/vs/workbench/common/composite.ts +++ b/src/vs/workbench/common/composite.ts @@ -35,7 +35,7 @@ export interface IComposite { /** * Returns the action item for a specific action. */ - getActionItem(action: IAction): IActionItem | null; + getActionItem(action: IAction): IActionItem | undefined; /** * Returns the underlying control of this composite. diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 56bc295b4c..8a3ae85ae2 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -187,13 +187,13 @@ export interface IEditorInputFactory { * Returns a string representation of the provided editor input that contains enough information * to deserialize back to the original editor input from the deserialize() method. */ - serialize(editorInput: EditorInput): string | null; + serialize(editorInput: EditorInput): string | undefined; /** * Returns an editor input from the provided serialized form of the editor input. This form matches * the value returned from the serialize() method. */ - deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | null; + deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | undefined; } export interface IUntitledResourceInput extends IBaseResourceInput { diff --git a/src/vs/workbench/common/editor/binaryEditorModel.ts b/src/vs/workbench/common/editor/binaryEditorModel.ts index fc7659d4e3..5336d9faca 100644 --- a/src/vs/workbench/common/editor/binaryEditorModel.ts +++ b/src/vs/workbench/common/editor/binaryEditorModel.ts @@ -76,7 +76,7 @@ export class BinaryEditorModel extends EditorModel { // Make sure to resolve up to date stat for file resources if (this.fileService.canHandleResource(this.resource)) { - return this.fileService.resolveFile(this.resource).then(stat => { + return this.fileService.resolveFile(this.resource, { resolveMetadata: true }).then(stat => { this.etag = stat.etag; if (typeof stat.size === 'number') { this.size = stat.size; diff --git a/src/vs/workbench/common/editor/dataUriEditorInput.ts b/src/vs/workbench/common/editor/dataUriEditorInput.ts index 1e33e6593e..c94c8635ec 100644 --- a/src/vs/workbench/common/editor/dataUriEditorInput.ts +++ b/src/vs/workbench/common/editor/dataUriEditorInput.ts @@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { DataUri } from 'vs/base/common/resources'; +import { withUndefinedAsNull } from 'vs/base/common/types'; /** * An editor input to present data URIs in a binary editor. Data URIs have the form of: @@ -51,11 +52,11 @@ export class DataUriEditorInput extends EditorInput { } getName(): string | null { - return this.name || null; + return withUndefinedAsNull(this.name); } getDescription(): string | null { - return this.description || null; + return withUndefinedAsNull(this.description); } resolve(): Promise { diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index 76b56fc5d4..252024d1ca 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -646,7 +646,7 @@ export class EditorGroup extends Disposable { let serializedEditors: ISerializedEditorInput[] = []; let serializablePreviewIndex: number | undefined; // {{SQL CARBON EDIT}} - let editors = this.editors.map(e => { + const editors = this.editors.map(e => { if (e instanceof QueryInput) { return e.sql; } else if (e instanceof NotebookInput) { @@ -655,7 +655,7 @@ export class EditorGroup extends Disposable { return e; }); editors.forEach(e => { - let factory = registry.getEditorInputFactory(e.getTypeId()); + const factory = registry.getEditorInputFactory(e.getTypeId()); if (factory) { // {{SQL CARBON EDIT}} // don't serialize unmodified unitited files @@ -665,7 +665,7 @@ export class EditorGroup extends Disposable { } // {{SQL CARBON EDIT}} - End - let value = factory.serialize(e); + const value = factory.serialize(e); if (typeof value === 'string') { serializedEditors.push({ id: e.getTypeId(), value }); serializableEditors.push(e); diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 0b76203552..4f882763c6 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -287,15 +287,15 @@ export const STATUS_BAR_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.hov }, nls.localize('statusBarItemHoverBackground', "Status bar item background color when hovering. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_PROMINENT_ITEM_BACKGROUND = registerColor('statusBarItem.prominentBackground', { - dark: '#388A34', - light: '#388A34', - hc: '#3883A4' + dark: Color.black.transparent(0.5), + light: Color.black.transparent(0.5), + hc: Color.black.transparent(0.5), }, nls.localize('statusBarProminentItemBackground', "Status bar prominent items background color. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.prominentHoverBackground', { - dark: '#369432', - light: '#369432', - hc: '#369432' + dark: Color.black.transparent(0.3), + light: Color.black.transparent(0.3), + hc: Color.black.transparent(0.3), }, nls.localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_HOST_NAME_BACKGROUND = registerColor('statusBarItem.hostBackground', { diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts index 8a3813d465..0b47ba7b46 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; @@ -123,7 +123,7 @@ registerEditorAction(class extends EditorAction { alias: 'Call Hierarchy', menuOpts: { group: 'navigation', - order: 111 + order: 1.48 }, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts index 5bf4e96e84..99f38f06f9 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/callHierarchy'; @@ -11,7 +11,7 @@ import { CallHierarchyProvider, CallHierarchyDirection, CallHierarchyItem } from import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { FuzzyScore } from 'vs/base/common/filters'; import * as callHTree from 'vs/workbench/contrib/callHierarchy/browser/callHierarchyTree'; -import { IAsyncDataTreeOptions } from 'vs/base/browser/ui/tree/asyncDataTree'; +import { IAsyncDataTreeOptions, IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; import { localize } from 'vs/nls'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -40,25 +40,29 @@ const enum State { Data = 'data' } -class ToggleHierarchyDirectionAction extends Action { +class ChangeHierarchyDirectionAction extends Action { - constructor(public direction: () => CallHierarchyDirection, callback: () => void) { - super('toggle.dir', undefined, 'call-hierarchy-toggle', true, () => { - callback(); - this._update(); + constructor(direction: CallHierarchyDirection, updateDirection: (direction: CallHierarchyDirection) => void) { + super('', undefined, '', true, () => { + if (direction === CallHierarchyDirection.CallsTo) { + direction = CallHierarchyDirection.CallsFrom; + } else { + direction = CallHierarchyDirection.CallsTo; + } + updateDirection(direction); + update(); return Promise.resolve(); }); - this._update(); - } - - private _update() { - if (this.direction() === CallHierarchyDirection.CallsFrom) { - this.label = localize('toggle.from', "Calls From..."); - this.checked = true; - } else { - this.label = localize('toggle.to', "Calls To..."); - this.checked = false; - } + const update = () => { + if (direction === CallHierarchyDirection.CallsFrom) { + this.label = localize('toggle.from', "Showing Calls"); + this.class = 'calls-from'; + } else { + this.label = localize('toggle.to', "Showing Callers"); + this.class = 'calls-to'; + } + }; + update(); } } @@ -86,11 +90,12 @@ class LayoutInfo { export class CallHierarchyTreePeekWidget extends PeekViewWidget { - private _toggleDirection: ToggleHierarchyDirectionAction; + private _changeDirectionAction: ChangeHierarchyDirectionAction; private _parent: HTMLElement; private _message: HTMLElement; private _splitView: SplitView; private _tree: WorkbenchAsyncDataTree; + private _treeViewStates = new Map(); private _editor: EmbeddedCodeEditorWidget; private _dim: Dimension; private _layoutInfo: LayoutInfo; @@ -311,7 +316,7 @@ export class CallHierarchyTreePeekWidget extends PeekViewWidget { this._tree.onDidChangeSelection(e => { const [element] = e.elements; // don't close on click - if (element && isNonEmptyArray(element.locations) && !(e.browserEvent instanceof MouseEvent)) { + if (element && isNonEmptyArray(element.locations) && e.browserEvent instanceof KeyboardEvent) { this.dispose(); this._editorService.openEditor({ resource: element.item.uri, @@ -338,7 +343,8 @@ export class CallHierarchyTreePeekWidget extends PeekViewWidget { async showItem(item: CallHierarchyItem): Promise { this._show(); - await this._tree.setInput(item); + const viewState = this._treeViewStates.get(this._direction); + await this._tree.setInput(item, viewState); const [root] = this._tree.getNode(item).children; await this._tree.expand(root.element as callHTree.Call); @@ -352,24 +358,26 @@ export class CallHierarchyTreePeekWidget extends PeekViewWidget { } else { this._parent.dataset['state'] = State.Data; this._tree.domFocus(); - this._tree.setFocus([firstChild]); + if (!viewState) { + this._tree.setFocus([firstChild]); + } this.setTitle( item.name, item.detail || this._labelService.getUriLabel(item.uri, { relative: true }), ); } - if (!this._toggleDirection) { - this._toggleDirection = new ToggleHierarchyDirectionAction( - () => this._direction, - () => { - let newDirection = this._direction === CallHierarchyDirection.CallsFrom ? CallHierarchyDirection.CallsTo : CallHierarchyDirection.CallsFrom; + if (!this._changeDirectionAction) { + const changeDirection = (newDirection: CallHierarchyDirection) => { + if (this._direction !== newDirection) { + this._treeViewStates.set(this._direction, this._tree.getViewState()); this._direction = newDirection; this.showItem(item); } - ); - this._actionbarWidget.push(this._toggleDirection, { label: false, icon: true }); - this._disposables.push(this._toggleDirection); + }; + this._changeDirectionAction = new ChangeHierarchyDirectionAction(this._direction, changeDirection); + this._disposables.push(this._changeDirectionAction); + this._actionbarWidget.push(this._changeDirectionAction, { icon: true, label: false }); } } diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts index b7b9dc6be1..dadd1f233d 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree'; @@ -12,6 +12,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { symbolKindToCssClass, Location } from 'vs/editor/common/modes'; import { ILabelService } from 'vs/platform/label/common/label'; import { Range } from 'vs/editor/common/core/range'; +import { hash } from 'vs/base/common/hash'; export class Call { constructor( @@ -52,7 +53,7 @@ export class SingleDirectionDataSource implements IAsyncDataSource { getId(element: Call): { toString(): string; } { - return element.item._id; + return hash(element.item.uri.toString(), hash(JSON.stringify(element.item.range))); } } diff --git a/src/vs/workbench/contrib/callHierarchy/browser/media/CallerOrCalleeView_16x.svg b/src/vs/workbench/contrib/callHierarchy/browser/media/CallerOrCalleeView_16x.svg deleted file mode 100644 index 7ae4f3a56e..0000000000 --- a/src/vs/workbench/contrib/callHierarchy/browser/media/CallerOrCalleeView_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/callHierarchy/browser/media/CallerOrCalleeView_16x_dark.svg b/src/vs/workbench/contrib/callHierarchy/browser/media/CallerOrCalleeView_16x_dark.svg deleted file mode 100644 index a6f9a99b55..0000000000 --- a/src/vs/workbench/contrib/callHierarchy/browser/media/CallerOrCalleeView_16x_dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/callHierarchy/browser/media/callHierarchy.css b/src/vs/workbench/contrib/callHierarchy/browser/media/callHierarchy.css index 20491c29c1..41b00fbf14 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/media/callHierarchy.css +++ b/src/vs/workbench/contrib/callHierarchy/browser/media/callHierarchy.css @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ .monaco-workbench .call-hierarchy .results, @@ -18,16 +18,18 @@ padding-top: 3em; } -.monaco-workbench .call-hierarchy-toggle { - background-image: url(CallerOrCalleeView_16x.svg); +.monaco-workbench .action-label.calls-to { + background-image: url(files_CallTo_CallTo_16x.svg); background-size: 14px 14px; background-repeat: no-repeat; background-position: left center; } -.vs-dark .monaco-workbench .call-hierarchy-toggle, -.hc-dark .monaco-workbench .call-hierarchy-toggle { - background-image: url(CallerOrCalleeView_16x_dark.svg); +.monaco-workbench .action-label.calls-from { + background-image: url(files_CallFrom_CallFrom_16x.svg); + background-size: 14px 14px; + background-repeat: no-repeat; + background-position: left center; } .monaco-workbench .call-hierarchy .monaco-split-view2.horizontal > .split-view-container > .split-view-view{ diff --git a/src/vs/workbench/contrib/callHierarchy/browser/media/files_CallFrom_CallFrom_16x.svg b/src/vs/workbench/contrib/callHierarchy/browser/media/files_CallFrom_CallFrom_16x.svg new file mode 100644 index 0000000000..667f3e7655 --- /dev/null +++ b/src/vs/workbench/contrib/callHierarchy/browser/media/files_CallFrom_CallFrom_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/contrib/callHierarchy/browser/media/files_CallTo_CallTo_16x.svg b/src/vs/workbench/contrib/callHierarchy/browser/media/files_CallTo_CallTo_16x.svg new file mode 100644 index 0000000000..26c436e77c --- /dev/null +++ b/src/vs/workbench/contrib/callHierarchy/browser/media/files_CallTo_CallTo_16x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts b/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts index 9587637463..8af10aff12 100644 --- a/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts +++ b/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { IPosition } from 'vs/editor/common/core/position'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts b/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts index 39a4d1afe6..c7b0cfcaef 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import './menuPreventer'; diff --git a/src/vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts index 3dc3a056ad..0b516a6477 100644 --- a/src/vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts @@ -60,7 +60,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget private _resizeObserver: any; private _onDidClose = new Emitter(); private _onDidCreateThread = new Emitter(); - private _isCollapsed; + private _isCollapsed: boolean; private _collapseAction: Action; private _commentGlyph?: CommentGlyphWidget; private _submitActionsDisposables: IDisposable[]; diff --git a/src/vs/workbench/contrib/debug/browser/debugToolbar.ts b/src/vs/workbench/contrib/debug/browser/debugToolbar.ts index eff448f75d..6119f9de7d 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolbar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolbar.ts @@ -95,7 +95,7 @@ export class DebugToolbar extends Themable implements IWorkbenchContribution { return new MenuItemActionItem(action, this.keybindingService, this.notificationService, contextMenuService); } - return null; + return undefined; } })); diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index eebecb50b5..3765e7f64b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -129,7 +129,7 @@ export class DebugViewlet extends ViewContainerViewlet { return [this.selectAndStartAction, this.configureAction, this.toggleReplAction]; } - getActionItem(action: IAction): IActionItem | null { + getActionItem(action: IAction): IActionItem | undefined { if (action.id === StartAction.ID) { this.startDebugActionItem = this.instantiationService.createInstance(StartDebugActionItem, null, action); return this.startDebugActionItem; @@ -141,7 +141,7 @@ export class DebugViewlet extends ViewContainerViewlet { return new MenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); } - return null; + return undefined; } focusView(id: string): void { diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index 2e475abbe6..94f8261b56 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -33,9 +33,13 @@ import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list import { dispose } from 'vs/base/common/lifecycle'; import { createMatches, FuzzyScore } from 'vs/base/common/filters'; import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; +import { ILabelService } from 'vs/platform/label/common/label'; const SMART = true; +// RFC 2396, Appendix A: https://www.ietf.org/rfc/rfc2396.txt +const URI_SCHEMA_PATTERN = /^[a-zA-Z][a-zA-Z0-9\+\-\.]+:/; + type LoadedScriptsItem = BaseTreeItem; class BaseTreeItem { @@ -217,7 +221,7 @@ class RootFolderTreeItem extends BaseTreeItem { class RootTreeItem extends BaseTreeItem { - constructor(private _debugModel: IDebugModel, private _environmentService: IEnvironmentService, private _contextService: IWorkspaceContextService) { + constructor(private _debugModel: IDebugModel, private _environmentService: IEnvironmentService, private _contextService: IWorkspaceContextService, private _labelService: ILabelService) { super(undefined, 'Root'); this._debugModel.getSessions().forEach(session => { this.add(session); @@ -225,7 +229,7 @@ class RootTreeItem extends BaseTreeItem { } add(session: IDebugSession): SessionTreeItem { - return this.createIfNeeded(session.getId(), () => new SessionTreeItem(this, session, this._environmentService, this._contextService)); + return this.createIfNeeded(session.getId(), () => new SessionTreeItem(this._labelService, this, session, this._environmentService, this._contextService)); } find(session: IDebugSession): SessionTreeItem { @@ -240,9 +244,11 @@ class SessionTreeItem extends BaseTreeItem { private _session: IDebugSession; private _initialized: boolean; private _map: Map; + private _labelService: ILabelService; - constructor(parent: BaseTreeItem, session: IDebugSession, private _environmentService: IEnvironmentService, private rootProvider: IWorkspaceContextService) { + constructor(labelService: ILabelService, parent: BaseTreeItem, session: IDebugSession, private _environmentService: IEnvironmentService, private rootProvider: IWorkspaceContextService) { super(parent, session.getLabel()); + this._labelService = labelService; this._initialized = false; this._session = session; this._map = new Map(); @@ -309,6 +315,10 @@ class SessionTreeItem extends BaseTreeItem { return; } + if (this._labelService && URI_SCHEMA_PATTERN.test(path)) { + path = this._labelService.getUriLabel(URI.parse(path)); + } + const match = SessionTreeItem.URL_REGEXP.exec(path); if (match && match.length === 3) { url = match[1]; @@ -390,6 +400,7 @@ export class LoadedScriptsView extends ViewletPanel { @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IDebugService private readonly debugService: IDebugService, + @ILabelService private readonly labelService: ILabelService ) { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService); this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService); @@ -403,7 +414,7 @@ export class LoadedScriptsView extends ViewletPanel { this.filter = new LoadedScriptsFilter(); - const root = new RootTreeItem(this.debugService.getModel(), this.environmentService, this.contextService); + const root = new RootTreeItem(this.debugService.getModel(), this.environmentService, this.contextService, this.labelService); this.treeLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility } as IResourceLabelsContainer); this.disposables.push(this.treeLabels); diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 50adf8350a..6bb4d122a4 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -309,12 +309,12 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati this.replInput.focus(); } - getActionItem(action: IAction): IActionItem | null { + getActionItem(action: IAction): IActionItem | undefined { if (action.id === SelectReplAction.ID) { return this.instantiationService.createInstance(SelectReplActionItem, this.selectReplAction); } - return null; + return undefined; } getActions(): IAction[] { diff --git a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts index 53c93567d9..772a9d368a 100644 --- a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts +++ b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; diff --git a/src/vs/workbench/contrib/debug/test/common/debugSource.test.ts b/src/vs/workbench/contrib/debug/test/common/debugSource.test.ts index eaed6ef761..58ddf12ab0 100644 --- a/src/vs/workbench/contrib/debug/test/common/debugSource.test.ts +++ b/src/vs/workbench/contrib/debug/test/common/debugSource.test.ts @@ -40,7 +40,7 @@ suite('Debug - Source', () => { }); test('get encoded debug data', () => { - const checkData = (uri: uri, expectedName, expectedPath, expectedSourceReference, expectedSessionId) => { + const checkData = (uri: uri, expectedName: string, expectedPath: string, expectedSourceReference: number | undefined, expectedSessionId?: number) => { let { name, path, sourceReference, sessionId } = Source.getEncodedDebugData(uri); assert.equal(name, expectedName); assert.equal(path, expectedPath); diff --git a/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts b/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts index e49282f398..d20c75d1e8 100644 --- a/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts +++ b/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts @@ -27,7 +27,7 @@ import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; // class MockGrammarContributions implements IGrammarContributions { - private scopeName; + private scopeName: string; constructor(scopeName: string) { this.scopeName = scopeName; diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts index 3d003ef3f1..70dd8e720f 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts @@ -7,9 +7,9 @@ import * as assert from 'assert'; import { Emitter } from 'vs/base/common/event'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; +import { INotificationService, IPromptChoice, Severity, IPromptOptions } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; -import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/electron-browser/experimentalPrompt'; @@ -59,11 +59,11 @@ suite('Experimental Prompts', () => { setup(() => { storageData = {}; - instantiationService.stub(IStorageService, { - get: (a, b, c) => a === 'experiments.experiment1' ? JSON.stringify(storageData) : c, + instantiationService.stub(IStorageService, >{ + get: (a: string, b: StorageScope, c?: string) => a === 'experiments.experiment1' ? JSON.stringify(storageData) : c, store: (a, b, c) => { if (a === 'experiments.experiment1') { - storageData = JSON.parse(b); + storageData = JSON.parse(b + ''); } } }); @@ -91,7 +91,7 @@ suite('Experimental Prompts', () => { }; instantiationService.stub(INotificationService, { - prompt: (a: Severity, b: string, c: IPromptChoice[], options) => { + prompt: (a: Severity, b: string, c: IPromptChoice[], options: IPromptOptions) => { assert.equal(b, promptText); assert.equal(c.length, 2); c[0].run(); @@ -116,7 +116,7 @@ suite('Experimental Prompts', () => { }; instantiationService.stub(INotificationService, { - prompt: (a: Severity, b: string, c: IPromptChoice[], options) => { + prompt: (a: Severity, b: string, c: IPromptChoice[]) => { assert.equal(b, promptText); assert.equal(c.length, 2); c[1].run(); @@ -141,10 +141,10 @@ suite('Experimental Prompts', () => { }; instantiationService.stub(INotificationService, { - prompt: (a: Severity, b: string, c: IPromptChoice[], options) => { + prompt: (a: Severity, b: string, c: IPromptChoice[], options: IPromptOptions) => { assert.equal(b, promptText); assert.equal(c.length, 2); - options.onCancel(); + options.onCancel!(); return undefined!; } }); @@ -194,4 +194,4 @@ suite('Experimental Prompts', () => { assert.equal(ExperimentalPrompts.getLocalizedText(englishUSTextCase.promptText, 'fr'), englishUSTextCase.promptText['en-us']); assert.equal(!!ExperimentalPrompts.getLocalizedText(noEnglishTextCase.promptText, 'fr'), false); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/extensions/common/extensionQuery.ts b/src/vs/workbench/contrib/extensions/common/extensionQuery.ts index d28c3a26f1..5da1cd517f 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionQuery.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionQuery.ts @@ -30,7 +30,7 @@ export class Query { return []; } if (subcommands[command]) { - return subcommands[command].map(subcommand => `@${command}:${subcommand}${subcommand === '' ? '' : ' '}`); + return subcommands[command].map((subcommand: string) => `@${command}:${subcommand}${subcommand === '' ? '' : ' '}`); } else { return [`@${command} `]; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts index f09e274cfd..58b552254a 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts @@ -51,7 +51,7 @@ import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry'; -import { isUndefined } from 'vs/base/common/types'; +import { isUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; // {{SQL CARBON EDIT}} @@ -258,7 +258,7 @@ export class ExtensionEditor extends BaseEditor { if (action instanceof ExtensionEditorDropDownAction) { return action.createActionItem(); } - return null; + return undefined; } }); @@ -729,7 +729,7 @@ export class ExtensionEditor extends BaseEditor { constructor(extension: IExtension, parent?: IExtensionData) { this.extension = extension; - this.parent = parent || null; + this.parent = withUndefinedAsNull(parent); } get hasChildren(): boolean { diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts index ac8f3008b8..cf1140492d 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts @@ -161,12 +161,14 @@ export class ExtensionsListView extends ViewletPanel { case 'name': options = assign(options, { sortBy: SortBy.Title }); break; } - const successCallback = model => { + const successCallback = (model: IPagedModel) => { this.queryRequest = null; this.setModel(model); return model; }; - const errorCallback = e => { + + + const errorCallback = (e: Error) => { const model = new PagedModel([]); if (!isPromiseCanceledError(e)) { this.queryRequest = null; @@ -534,7 +536,7 @@ export class ExtensionsListView extends ViewletPanel { return Promise.all([othersPromise, workspacePromise]) .then(([others, workspaceRecommendations]) => { fileBasedRecommendations = fileBasedRecommendations.filter(x => workspaceRecommendations.every(({ extensionId }) => x.extensionId !== extensionId)); - others = others.filter(x => x => workspaceRecommendations.every(({ extensionId }) => x.extensionId !== extensionId)); + others = others.filter(x => workspaceRecommendations.every(({ extensionId }) => x.extensionId !== extensionId)); const names = this.getTrimmedRecommendations(local, value, fileBasedRecommendations, others, []); const recommendationsWithReason = this.tipsService.getAllRecommendationsWithReason(); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsWidgets.ts index 930bd9c12f..94ac13a551 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsWidgets.ts @@ -12,7 +12,7 @@ import { localize } from 'vs/nls'; import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ILabelService } from 'vs/platform/label/common/label'; import { extensionButtonProminentBackground, extensionButtonProminentForeground } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; @@ -178,7 +178,7 @@ export class RecommendationWidget extends ExtensionWidget { this.element = append(this.parent, $('div.bookmark')); const recommendation = append(this.element, $('.recommendation')); append(recommendation, $('span.octicon.octicon-star')); - const applyBookmarkStyle = (theme) => { + const applyBookmarkStyle = (theme: ITheme) => { const bgColor = theme.getColor(extensionButtonProminentBackground); const fgColor = theme.getColor(extensionButtonProminentForeground); recommendation.style.borderTopColor = bgColor ? bgColor.toString() : 'transparent'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 88fab804bd..d0a14fb49e 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -69,9 +69,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { instantiationService.stub(ISharedProcessService, TestSharedProcessService); instantiationService.stub(IWorkspaceContextService, new TestContextService()); - instantiationService.stub(IConfigurationService, { + instantiationService.stub(IConfigurationService, >{ onDidChangeConfiguration: () => { return undefined!; }, - getValue: (key?) => { + getValue: (key?: string) => { return (key === AutoCheckUpdatesConfigurationKey || key === AutoUpdateConfigurationKey) ? true : undefined; } }); diff --git a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts index ba53450975..3a89ebd105 100644 --- a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts @@ -27,6 +27,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ResourceQueue, timeout } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { withNullAsUndefined } from 'vs/base/common/types'; // {{SQL CARBON EDIT}} import { QueryInput } from 'sql/parts/query/common/queryInput'; @@ -284,7 +285,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut if (editorResource && resource.toString() === editorResource.toString()) { const control = editor.getControl(); if (isCodeEditor(control)) { - return control.saveViewState() || undefined; + return withNullAsUndefined(control.saveViewState()); } } } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 54ef1e763f..877be21968 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -144,7 +144,7 @@ export class NewFileAction extends BaseErrorReportingAction { } const stat = new ExplorerItem(PLACEHOLDER_URI, folder, false); - return folder.fetchChildren(this.fileService).then(() => { + return folder.fetchChildren(this.fileService, this.explorerService).then(() => { folder.addChild(stat); const onSuccess = (value: string) => { @@ -212,7 +212,7 @@ export class NewFolderAction extends BaseErrorReportingAction { } const stat = new ExplorerItem(PLACEHOLDER_URI, folder, true); - return folder.fetchChildren(this.fileService).then(() => { + return folder.fetchChildren(this.fileService, this.explorerService).then(() => { folder.addChild(stat); const onSuccess = (value: string) => { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 4ade85099a..62d0d6498b 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as perf from 'vs/base/common/performance'; -import { sequence } from 'vs/base/common/async'; import { Action, IAction } from 'vs/base/common/actions'; import { memoize } from 'vs/base/common/decorators'; import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService, ExplorerResourceCut } from 'vs/workbench/contrib/files/common/files'; @@ -49,6 +48,9 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { isMacintosh } from 'vs/base/common/platform'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { isEqualOrParent } from 'vs/base/common/resources'; +import { values } from 'vs/base/common/map'; +import { first } from 'vs/base/common/arrays'; import { withNullAsUndefined } from 'vs/base/common/types'; export class ExplorerView extends ViewletPanel { @@ -188,7 +190,7 @@ export class ExplorerView extends ViewletPanel { this.tree.domFocus(); } })); - this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal))); + this.disposables.push(this.explorerService.onDidSelectResource(e => this.onSelectResource(e.resource, e.reveal))); this.disposables.push(this.explorerService.onDidCopyItems(e => this.onCopyItems(e.items, e.cut, e.previouslyCutItems))); // Update configuration @@ -507,27 +509,27 @@ export class ExplorerView extends ViewletPanel { return withNullAsUndefined(toResource(input, { supportSideBySide: true })); } - private onSelectItem(fileStat: ExplorerItem | undefined, reveal = this.autoReveal): Promise { - if (!fileStat || !this.isBodyVisible() || this.tree.getInput() === fileStat) { - return Promise.resolve(undefined); + private async onSelectResource(resource: URI | undefined, reveal = this.autoReveal): Promise { + if (!resource || !this.isBodyVisible()) { + return; } // Expand all stats in the parent chain - const toExpand: ExplorerItem[] = []; - let parent = fileStat.parent; - while (parent) { - toExpand.push(parent); - parent = parent.parent; + let item: ExplorerItem | undefined = this.explorerService.roots.filter(i => isEqualOrParent(resource, i.resource))[0]; + + while (item && item.resource.toString() !== resource.toString()) { + await this.tree.expand(item); + item = first(values(item.children), i => isEqualOrParent(resource, i.resource)); } - return sequence(toExpand.reverse().map(s => () => this.tree.expand(s))).then(() => { + if (item && item.parent) { if (reveal) { - this.tree.reveal(fileStat, 0.5); + this.tree.reveal(item, 0.5); } - this.tree.setFocus([fileStat]); - this.tree.setSelection([fileStat]); - }); + this.tree.setFocus([item]); + this.tree.setSelection([item]); + } } private onCopyItems(stats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 08c58b59d5..e6c3314957 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -9,7 +9,7 @@ import * as glob from 'vs/base/common/glob'; import { IListVirtualDelegate, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IFileService, FileKind, IFileStat, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; +import { IFileService, FileKind, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDisposable, Disposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; @@ -63,10 +63,11 @@ export class ExplorerDelegate implements IListVirtualDelegate { export class ExplorerDataSource implements IAsyncDataSource { constructor( - @IProgressService private progressService: IProgressService, - @INotificationService private notificationService: INotificationService, - @IWorkbenchLayoutService private layoutService: IWorkbenchLayoutService, - @IFileService private fileService: IFileService + @IProgressService private readonly progressService: IProgressService, + @INotificationService private readonly notificationService: INotificationService, + @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, + @IFileService private readonly fileService: IFileService, + @IExplorerService private readonly explorerService: IExplorerService ) { } hasChildren(element: ExplorerItem | ExplorerItem[]): boolean { @@ -78,7 +79,7 @@ export class ExplorerDataSource implements IAsyncDataSource { + const promise = element.fetchChildren(this.fileService, this.explorerService).then(undefined, e => { // Do not show error for roots since we already use an explorer decoration to notify user if (!(element instanceof ExplorerItem && element.isRoot)) { this.notificationService.error(e); @@ -635,12 +636,12 @@ export class FileDragAndDrop implements ITreeDragAndDrop { if (resources && resources.length > 0) { // Resolve target to check for name collisions and ask user - return this.fileService.resolveFile(target.resource).then((targetStat: IFileStat) => { + return this.fileService.resolveFile(target.resource).then(targetStat => { // Check for name collisions const targetNames = new Set(); if (targetStat.children) { - targetStat.children.forEach((child) => { + targetStat.children.forEach(child => { targetNames.add(isLinux ? child.name : child.name.toLowerCase()); }); } diff --git a/src/vs/workbench/contrib/files/common/explorerModel.ts b/src/vs/workbench/contrib/files/common/explorerModel.ts index cf4b814556..a6b96c3614 100644 --- a/src/vs/workbench/contrib/files/common/explorerModel.ts +++ b/src/vs/workbench/contrib/files/common/explorerModel.ts @@ -16,6 +16,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { memoize } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; +import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; export class ExplorerModel implements IDisposable { @@ -85,7 +86,6 @@ export class ExplorerItem { private _isReadonly?: boolean, private _name: string = resources.basenameOrAuthority(resource), private _mtime?: number, - private _etag?: string, ) { this._isDirectoryResolved = false; } @@ -106,10 +106,6 @@ export class ExplorerItem { return !!this._isReadonly; } - get etag(): string | undefined { - return this._etag; - } - get mtime(): number | undefined { return this._mtime; } @@ -154,7 +150,7 @@ export class ExplorerItem { } static create(raw: IFileStat, parent: ExplorerItem | undefined, resolveTo?: URI[]): ExplorerItem { - const stat = new ExplorerItem(raw.resource, parent, raw.isDirectory, raw.isSymbolicLink, raw.isReadonly, raw.name, raw.mtime, raw.etag); + const stat = new ExplorerItem(raw.resource, parent, raw.isDirectory, raw.isSymbolicLink, raw.isReadonly, raw.name, raw.mtime); // Recursively add children if present if (stat.isDirectory) { @@ -247,10 +243,13 @@ export class ExplorerItem { return this.children.get(this.getPlatformAwareName(name)); } - fetchChildren(fileService: IFileService): Promise { + fetchChildren(fileService: IFileService, explorerService: IExplorerService): Promise { let promise: Promise = Promise.resolve(undefined); if (!this._isDirectoryResolved) { - promise = fileService.resolveFile(this.resource, { resolveSingleChildDescendants: true }).then(stat => { + // Resolve metadata only when the mtime is needed since this can be expensive + // Mtime is only used when the sort order is 'modified' + const resolveMetadata = explorerService.sortOrder === 'modified'; + promise = fileService.resolveFile(this.resource, { resolveSingleChildDescendants: true, resolveMetadata }).then(stat => { const resolved = ExplorerItem.create(stat, this); ExplorerItem.mergeLocalWithDisk(resolved, this); this._isDirectoryResolved = true; diff --git a/src/vs/workbench/contrib/files/common/explorerService.ts b/src/vs/workbench/contrib/files/common/explorerService.ts index 32d5923c45..6f1d459994 100644 --- a/src/vs/workbench/contrib/files/common/explorerService.ts +++ b/src/vs/workbench/contrib/files/common/explorerService.ts @@ -34,7 +34,7 @@ export class ExplorerService implements IExplorerService { private _onDidChangeRoots = new Emitter(); private _onDidChangeItem = new Emitter(); private _onDidChangeEditable = new Emitter(); - private _onDidSelectItem = new Emitter<{ item?: ExplorerItem, reveal?: boolean }>(); + private _onDidSelectResource = new Emitter<{ resource?: URI, reveal?: boolean }>(); private _onDidCopyItems = new Emitter<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>(); private disposables: IDisposable[] = []; private editable: { stat: ExplorerItem, data: IEditableData } | undefined; @@ -68,8 +68,8 @@ export class ExplorerService implements IExplorerService { return this._onDidChangeEditable.event; } - get onDidSelectItem(): Event<{ item?: ExplorerItem, reveal?: boolean }> { - return this._onDidSelectItem.event; + get onDidSelectResource(): Event<{ resource?: URI, reveal?: boolean }> { + return this._onDidSelectResource.event; } get onDidCopyItems(): Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }> { @@ -142,12 +142,12 @@ export class ExplorerService implements IExplorerService { select(resource: URI, reveal?: boolean): Promise { const fileStat = this.findClosest(resource); if (fileStat) { - this._onDidSelectItem.fire({ item: fileStat, reveal }); + this._onDidSelectResource.fire({ resource: fileStat.resource, reveal }); return Promise.resolve(undefined); } // Stat needs to be resolved first and then revealed - const options: IResolveFileOptions = { resolveTo: [resource] }; + const options: IResolveFileOptions = { resolveTo: [resource], resolveMetadata: false }; const workspaceFolder = this.contextService.getWorkspaceFolder(resource); const rootUri = workspaceFolder ? workspaceFolder.uri : this.roots[0].resource; const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop()!; @@ -161,7 +161,7 @@ export class ExplorerService implements IExplorerService { this._onDidChangeItem.fire(item ? item.parent : undefined); // Select and Reveal - this._onDidSelectItem.fire({ item: item || undefined, reveal }); + this._onDidSelectResource.fire({ resource: item ? item.resource : undefined, reveal }); }, () => { root.isError = true; this._onDidChangeItem.fire(root); @@ -192,7 +192,8 @@ export class ExplorerService implements IExplorerService { // Add the new file to its parent (Model) parents.forEach(p => { // We have to check if the parent is resolved #29177 - const thenable: Promise = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolveFile(p.resource); + const resolveMetadata = this.sortOrder === `modified`; + const thenable: Promise = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolveFile(p.resource, { resolveMetadata }); thenable.then(stat => { if (stat) { const modelStat = ExplorerItem.create(stat, p.parent); @@ -357,10 +358,10 @@ export class ExplorerService implements IExplorerService { private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; if (this._sortOrder !== configSortOrder) { - const shouldFire = this._sortOrder !== undefined; + const shouldRefresh = this._sortOrder !== undefined; this._sortOrder = configSortOrder; - if (shouldFire) { - this._onDidChangeRoots.fire(); + if (shouldRefresh) { + this.refresh(); } } } diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index 1375206aeb..a322521942 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -44,7 +44,7 @@ export interface IExplorerService { readonly onDidChangeRoots: Event; readonly onDidChangeItem: Event; readonly onDidChangeEditable: Event; - readonly onDidSelectItem: Event<{ item?: ExplorerItem, reveal?: boolean }>; + readonly onDidSelectResource: Event<{ resource?: URI, reveal?: boolean }>; readonly onDidCopyItems: Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>; setEditable(stat: ExplorerItem, data: IEditableData | null): void; diff --git a/src/vs/workbench/contrib/files/test/electron-browser/explorerModel.test.ts b/src/vs/workbench/contrib/files/test/electron-browser/explorerModel.test.ts index 6f3657563e..ed1869f8dd 100644 --- a/src/vs/workbench/contrib/files/test/electron-browser/explorerModel.test.ts +++ b/src/vs/workbench/contrib/files/test/electron-browser/explorerModel.test.ts @@ -147,7 +147,6 @@ suite('Files - View Model', function () { assert.strictEqual(s1.find(toResource.call(this, 'foobar')), null); assert.strictEqual(s1.find(toResource.call(this, '/')), s1); - // assert.strictEqual(s1.find(toResource.call(this, '')), s1); //TODO@isidor this fails with proper paths usage }); test('Find with mixed case', function () { @@ -244,21 +243,19 @@ suite('Files - View Model', function () { }); test('Merge Local with Disk', function () { - const d = new Date().toUTCString(); - - const merge1 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, true, false, false, 'to', Date.now(), d); - const merge2 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, true, false, false, 'to', Date.now(), new Date(0).toUTCString()); + const merge1 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, true, false, false, 'to', Date.now()); + const merge2 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, true, false, false, 'to', Date.now()); // Merge Properties ExplorerItem.mergeLocalWithDisk(merge2, merge1); assert.strictEqual(merge1.mtime, merge2.mtime); // Merge Child when isDirectoryResolved=false is a no-op - merge2.addChild(new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, true, false, false, 'foo.html', Date.now(), d)); + merge2.addChild(new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, true, false, false, 'foo.html', Date.now())); ExplorerItem.mergeLocalWithDisk(merge2, merge1); // Merge Child with isDirectoryResolved=true - const child = new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, true, false, false, 'foo.html', Date.now(), d); + const child = new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, true, false, false, 'foo.html', Date.now()); merge2.removeChild(child); merge2.addChild(child); (merge2)._isDirectoryResolved = true; diff --git a/src/vs/workbench/contrib/format/browser/format.contribution.ts b/src/vs/workbench/contrib/format/browser/format.contribution.ts index 3e9036281c..2b48a553eb 100644 --- a/src/vs/workbench/contrib/format/browser/format.contribution.ts +++ b/src/vs/workbench/contrib/format/browser/format.contribution.ts @@ -3,57 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { setFormatterConflictCallback, FormatMode, FormatKind } from 'vs/editor/contrib/format/format'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { localize } from 'vs/nls'; -import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/extensions'; - - -class FormattingConflictHandler { - - private _registration: IDisposable; - - constructor( - @INotificationService notificationService: INotificationService, - @IViewletService private readonly _viewletService: IViewletService, - ) { - - this._registration = setFormatterConflictCallback((ids, model, mode) => { - if (mode & FormatMode.Auto) { - return; - } - if (ids.length === 0) { - const langName = model.getLanguageIdentifier().language; - const message = mode & FormatKind.Document - ? localize('no.documentprovider', "There is no document formatter for '{0}'-files installed.", langName) - : localize('no.selectionprovider', "There is no selection formatter for '{0}'-files installed.", langName); - - const choice = { - label: localize('install.formatter', "Install Formatter..."), - run: () => { - return this._viewletService.openViewlet(VIEWLET_ID, true).then(viewlet => { - if (viewlet) { - (viewlet as IExtensionsViewlet).search(`category:formatters ${langName}`); - } - }); - } - }; - notificationService.prompt(Severity.Info, message, [choice]); - } - }); - } - - dispose(): void { - this._registration.dispose(); - } -} - -Registry.as(Extensions.Workbench).registerWorkbenchContribution( - FormattingConflictHandler, - LifecyclePhase.Restored -); +import 'vs/css!./media/format.contribution'; +import './formatActionsMultiple'; +import './formatActionsNone'; diff --git a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts new file mode 100644 index 0000000000..1543776014 --- /dev/null +++ b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts @@ -0,0 +1,138 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { DocumentRangeFormattingEditProviderRegistry } from 'vs/editor/common/modes'; +import * as nls from 'vs/nls'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IQuickInputService, IQuickPickItem, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { formatDocumentRangeWithProvider, formatDocumentWithProvider, getRealAndSyntheticDocumentFormattersOrdered } from 'vs/editor/contrib/format/format'; +import { Range } from 'vs/editor/common/core/range'; +import { showExtensionQuery } from 'vs/workbench/contrib/format/browser/showExtensionQuery'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; + +interface IIndexedPick extends IQuickPickItem { + index: number; +} + +const openExtensionAction: IQuickInputButton = { + tooltip: nls.localize('show.ext', "Show extension..."), + iconClass: 'format-show-extension' +}; + +registerEditorAction(class FormatDocumentMultipleAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.formatDocument.multiple', + label: nls.localize('formatDocument.label.multiple', "Format Document..."), + alias: 'Format Document...', + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasMultipleDocumentFormattingProvider), + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }, + weight: KeybindingWeight.EditorContrib, + }, + menuOpts: { + group: '1_modification', + order: 1.3 + } + }); + } + + async run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): Promise { + if (!editor.hasModel()) { + return; + } + const instaService = accessor.get(IInstantiationService); + const quickPickService = accessor.get(IQuickInputService); + const viewletService = accessor.get(IViewletService); + const model = editor.getModel(); + + const provider = getRealAndSyntheticDocumentFormattersOrdered(model); + const picks = provider.map((provider, index) => { + return { + index, + label: provider.displayName || '', + buttons: [openExtensionAction] + }; + }); + + const pick = await quickPickService.pick(picks, { + placeHolder: nls.localize('format.placeHolder', "Select a formatter"), + onDidTriggerItemButton: (e) => { + const { extensionId } = provider[e.item.index]; + return showExtensionQuery(viewletService, `@id:${extensionId!.value}`); + } + }); + if (pick) { + await instaService.invokeFunction(formatDocumentWithProvider, provider[pick.index], editor, CancellationToken.None); + } + } +}); + +registerEditorAction(class FormatSelectionMultipleAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.formatSelection.multiple', + label: nls.localize('formatSelection.label.multiple', "Format Selection..."), + alias: 'Format Code...', + precondition: ContextKeyExpr.and(ContextKeyExpr.and(EditorContextKeys.writable), EditorContextKeys.hasMultipleDocumentSelectionFormattingProvider), + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F), + weight: KeybindingWeight.EditorContrib + }, + menuOpts: { + when: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection), + group: '1_modification', + order: 1.31 + } + }); + } + + async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + if (!editor.hasModel()) { + return; + } + const instaService = accessor.get(IInstantiationService); + const quickPickService = accessor.get(IQuickInputService); + const viewletService = accessor.get(IViewletService); + const model = editor.getModel(); + + let range: Range = editor.getSelection(); + if (range.isEmpty()) { + range = new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber)); + } + + const provider = DocumentRangeFormattingEditProviderRegistry.ordered(model); + const picks = provider.map((provider, index) => { + return { + index, + label: provider.displayName || '', + buttons: [openExtensionAction] + }; + }); + + const pick = await quickPickService.pick(picks, { + placeHolder: nls.localize('format.placeHolder', "Select a formatter"), + onDidTriggerItemButton: (e) => { + const { extensionId } = provider[e.item.index]; + return showExtensionQuery(viewletService, `@id:${extensionId!.value}`); + } + }); + if (pick) { + await instaService.invokeFunction(formatDocumentRangeWithProvider, provider[pick.index], editor, range, CancellationToken.None); + } + } +}); diff --git a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts new file mode 100644 index 0000000000..9fc57ae620 --- /dev/null +++ b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { DocumentFormattingEditProviderRegistry } from 'vs/editor/common/modes'; +import * as nls from 'vs/nls'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { showExtensionQuery } from 'vs/workbench/contrib/format/browser/showExtensionQuery'; + +registerEditorAction(class FormatDocumentMultipleAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.formatDocument.none', + label: nls.localize('formatDocument.label.multiple', "Format Document"), + alias: 'Format Document', + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider.toNegated()), + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentFormattingProvider.toNegated()), + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }, + weight: KeybindingWeight.EditorContrib, + } + }); + } + + async run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): Promise { + if (!editor.hasModel()) { + return; + } + + const commandService = accessor.get(ICommandService); + const viewletService = accessor.get(IViewletService); + const notificationService = accessor.get(INotificationService); + const model = editor.getModel(); + const formatterCount = DocumentFormattingEditProviderRegistry.all(model).length; + + if (formatterCount > 1) { + return commandService.executeCommand('editor.action.formatDocument.multiple'); + } else if (formatterCount === 1) { + return commandService.executeCommand('editor.action.formatDocument'); + } else { + const langName = model.getLanguageIdentifier().language; + const message = nls.localize('no.rovider', "There is no formatter for '{0}'-files installed.", langName); + const choice = { + label: nls.localize('install.formatter', "Install Formatter..."), + run: () => showExtensionQuery(viewletService, `category:formatters ${langName}`) + }; + notificationService.prompt(Severity.Info, message, [choice]); + } + } +}); diff --git a/src/vs/workbench/contrib/format/browser/media/configure-inverse.svg b/src/vs/workbench/contrib/format/browser/media/configure-inverse.svg new file mode 100644 index 0000000000..61baaea2b8 --- /dev/null +++ b/src/vs/workbench/contrib/format/browser/media/configure-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/contrib/format/browser/media/configure.svg b/src/vs/workbench/contrib/format/browser/media/configure.svg new file mode 100644 index 0000000000..3dec2ba50f --- /dev/null +++ b/src/vs/workbench/contrib/format/browser/media/configure.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/contrib/format/browser/media/format.contribution.css b/src/vs/workbench/contrib/format/browser/media/format.contribution.css new file mode 100644 index 0000000000..74dab0e2bb --- /dev/null +++ b/src/vs/workbench/contrib/format/browser/media/format.contribution.css @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .format-show-extension { + background-image: url('configure.svg'); +} + +.vs-dark .monaco-workbench .format-show-extension, +.hc-black .monaco-workbench .format-show-extension { + background-image: url('configure-inverse.svg'); +} diff --git a/src/vs/workbench/contrib/format/browser/showExtensionQuery.ts b/src/vs/workbench/contrib/format/browser/showExtensionQuery.ts new file mode 100644 index 0000000000..21c86acddb --- /dev/null +++ b/src/vs/workbench/contrib/format/browser/showExtensionQuery.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/extensions'; + +export function showExtensionQuery(viewletService: IViewletService, query: string) { + return viewletService.openViewlet(VIEWLET_ID, true).then(viewlet => { + if (viewlet) { + (viewlet as IExtensionsViewlet).search(query); + } + }); +} diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 99d8727189..77d624ed67 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -38,7 +38,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { outputChannelRegistry.registerChannel({ id: Constants.sqlToolsLogChannellId, label: nls.localize('sqlToolsLog', "Log (SqlTools)"), file: URI.file(toolsServiceLogFile), log: true }); // {{SQL CARBON EDIT}} - End - const registerTelemetryChannel = (level) => { + const registerTelemetryChannel = (level: LogLevel) => { if (level === LogLevel.Trace && !outputChannelRegistry.getChannel(Constants.telemetryLogChannelId)) { outputChannelRegistry.registerChannel({ id: Constants.telemetryLogChannelId, label: nls.localize('telemetryLog', "Telemetry"), file: URI.file(join(environmentService.logsPath, `telemetry.log`)), log: true }); } diff --git a/src/vs/workbench/contrib/markers/browser/markersModel.ts b/src/vs/workbench/contrib/markers/browser/markersModel.ts index 67c57c1c84..877b89ccb8 100644 --- a/src/vs/workbench/contrib/markers/browser/markersModel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersModel.ts @@ -12,6 +12,7 @@ import { values } from 'vs/base/common/map'; import { memoize } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; import { Hasher } from 'vs/base/common/hash'; +import { withUndefinedAsNull } from 'vs/base/common/types'; function compareUris(a: URI, b: URI) { const astr = a.toString(); @@ -138,7 +139,7 @@ export class MarkersModel { } getResourceMarkers(resource: URI): ResourceMarkers | null { - return this.resourcesByUri.get(resource.toString()) || null; + return withUndefinedAsNull(this.resourcesByUri.get(resource.toString())); } setResourceMarkers(resource: URI, rawMarkers: IMarker[]): void { diff --git a/src/vs/workbench/contrib/markers/browser/markersPanel.ts b/src/vs/workbench/contrib/markers/browser/markersPanel.ts index 50df818981..c41992654a 100644 --- a/src/vs/workbench/contrib/markers/browser/markersPanel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersPanel.ts @@ -628,7 +628,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { if (keybinding) { return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() }); } - return null; + return undefined; }, onHide: (wasCancelled?: boolean) => { if (wasCancelled) { @@ -670,7 +670,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { return this.tree.getFocus()[0]; } - public getActionItem(action: IAction): IActionItem | null { + public getActionItem(action: IAction): IActionItem | undefined { if (action.id === MarkersFilterAction.ID) { this.filterInputActionItem = this.instantiationService.createInstance(MarkersFilterActionItem, this.filterAction, this); return this.filterInputActionItem; diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 333c58610d..b3536f4ec0 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -253,7 +253,7 @@ class MarkerWidget extends Disposable { ) { super(); this.actionBar = this._register(new ActionBar(dom.append(parent, dom.$('.actions')), { - actionItemProvider: (action) => action.id === QuickFixAction.ID ? instantiationService.createInstance(QuickFixActionItem, action) : null + actionItemProvider: (action) => action.id === QuickFixAction.ID ? instantiationService.createInstance(QuickFixActionItem, action) : undefined })); this.icon = dom.append(parent, dom.$('.icon')); this.multilineActionbar = this._register(new ActionBar(dom.append(parent, dom.$('.multiline-actions')))); diff --git a/src/vs/workbench/contrib/output/browser/outputPanel.ts b/src/vs/workbench/contrib/output/browser/outputPanel.ts index 3ec1479464..2cbcd1b399 100644 --- a/src/vs/workbench/contrib/output/browser/outputPanel.ts +++ b/src/vs/workbench/contrib/output/browser/outputPanel.ts @@ -75,7 +75,7 @@ export class OutputPanel extends AbstractTextResourceEditor { return this.actions; } - public getActionItem(action: Action): IActionItem | null { + public getActionItem(action: Action): IActionItem | undefined { if (action.id === SwitchOutputAction.ID) { return this.instantiationService.createInstance(SwitchOutputActionItem, action); } diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index c0a4c57226..c0f4b2ee2a 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -66,7 +66,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo private channels: Map = new Map(); private activeChannelIdInStorage: string; - private activeChannel: OutputChannel | null; + private activeChannel?: OutputChannel; private readonly _onActiveOutputChannel = new Emitter(); readonly onActiveOutputChannel: Event = this._onActiveOutputChannel.event; @@ -105,7 +105,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo // Set active channel to first channel if not set if (!this.activeChannel) { const channels = this.getChannelDescriptors(); - this.activeChannel = channels && channels.length > 0 ? this.getChannel(channels[0].id) : null; + this.activeChannel = channels && channels.length > 0 ? this.getChannel(channels[0].id) : undefined; } this._register(this.lifecycleService.onShutdown(() => this.dispose())); @@ -140,15 +140,15 @@ export class OutputService extends Disposable implements IOutputService, ITextMo return promise.then(() => this._onActiveOutputChannel.fire(id)); } - getChannel(id: string): OutputChannel | null { - return this.channels.get(id) || null; + getChannel(id: string): OutputChannel | undefined { + return this.channels.get(id); } getChannelDescriptors(): IOutputChannelDescriptor[] { return Registry.as(Extensions.OutputChannels).getChannels(); } - getActiveChannel(): IOutputChannel | null { + getActiveChannel(): IOutputChannel | undefined { return this.activeChannel; } @@ -194,7 +194,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo channel.model.onDispose(() => { if (this.activeChannel === channel) { const channels = this.getChannelDescriptors(); - const channel = channels.length ? this.getChannel(channels[0].id) : null; + const channel = channels.length ? this.getChannel(channels[0].id) : undefined; if (channel && this.isPanelShown()) { this.showChannel(channel.id, true); } else { diff --git a/src/vs/workbench/contrib/output/common/output.ts b/src/vs/workbench/contrib/output/common/output.ts index 9c67aaab10..3e4746d488 100644 --- a/src/vs/workbench/contrib/output/common/output.ts +++ b/src/vs/workbench/contrib/output/common/output.ts @@ -68,7 +68,7 @@ export interface IOutputService { * Given the channel id returns the output channel instance. * Channel should be first registered via OutputChannelRegistry. */ - getChannel(id: string): IOutputChannel | null; + getChannel(id: string): IOutputChannel | undefined; /** * Returns an array of all known output channels descriptors. @@ -79,7 +79,7 @@ export interface IOutputService { * Returns the currently active channel. * Only one channel can be active at a given moment. */ - getActiveChannel(): IOutputChannel | null; + getActiveChannel(): IOutputChannel | undefined; /** * Show the channel with the passed id. diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index 88b3b31e84..470ba29d3d 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -376,7 +376,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor if (action.id === this.recordKeysAction.id) { return new CheckboxActionItem(null, action); } - return null; + return undefined; } })); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 2100b8c41e..65099dc618 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -424,7 +424,7 @@ export class FolderSettingsActionItem extends BaseActionItem { this.contextMenuService.showContextMenu({ getAnchor: () => this.container, getActions: () => this.getDropdownMenuActions(), - getActionItem: () => null, + getActionItem: () => undefined, onHide: () => { this.anchorElement.blur(); } @@ -495,7 +495,7 @@ export class SettingsTargetsWidget extends Widget { orientation: ActionsOrientation.HORIZONTAL, ariaLabel: localize('settingsSwitcherBarAriaLabel', "Settings Switcher"), animated: false, - actionItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : null + actionItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : undefined })); this.userSettings = new Action('userSettings', localize('userSettings', "User Settings"), '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER)); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 4c0d2986b9..60e2b59936 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -439,7 +439,7 @@ export abstract class AbstractSettingRenderer implements ITreeRenderer this._onDidChangeSetting.fire({ key: element.setting.key, value, type: template.context!.valueType }); + const onChange = (value: any) => this._onDidChangeSetting.fire({ key: element.setting.key, value, type: template.context!.valueType }); template.deprecationWarningElement.innerText = element.setting.deprecationMessage || ''; this.renderValue(element, template, onChange); @@ -698,7 +698,7 @@ export class SettingExcludeRenderer extends AbstractSettingRenderer implements I } } - const sortKeys = (obj) => { + const sortKeys = (obj: Object) => { const keyArray = Object.keys(obj) .map(key => ({ key, val: obj[key] })) .sort((a, b) => a.key.localeCompare(b.key)); @@ -901,7 +901,7 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT ? parseInt : parseFloat; const nullNumParseFn = (dataElement.valueType === 'nullable-integer' || dataElement.valueType === 'nullable-number') - ? (v => v === '' ? null : numParseFn(v)) : numParseFn; + ? ((v: string) => v === '' ? null : numParseFn(v)) : numParseFn; const label = this.setElementAriaLabels(dataElement, SETTINGS_NUMBER_TEMPLATE_ID, template); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index 22fb43d7ac..312fbed6b9 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as arrays from 'vs/base/common/arrays'; -import { isArray } from 'vs/base/common/types'; +import { isArray, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -262,11 +262,11 @@ export class SettingsTreeModel { } getElementById(id: string): SettingsTreeElement | null { - return this._treeElementsById.get(id) || null; + return withUndefinedAsNull(this._treeElementsById.get(id)); } getElementsByName(name: string): SettingsTreeSettingElement[] | null { - return this._treeElementsBySettingName.get(name) || null; + return withUndefinedAsNull(this._treeElementsBySettingName.get(name)); } updateElementsByName(name: string): void { @@ -378,7 +378,7 @@ function wordifyKey(key: string): string { } function trimCategoryForGroup(category: string, groupId: string): string { - const doTrim = forward => { + const doTrim = (forward: boolean) => { const parts = groupId.split('.'); while (parts.length) { const reg = new RegExp(`^${parts.join('\\.')}(\\.|$)`, 'i'); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index f845345409..e0b861c524 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -403,7 +403,7 @@ export class ExcludeSettingWidget extends Disposable { private renderEditItem(item: IExcludeViewItem): HTMLElement { const rowElement = $('.setting-exclude-edit-row'); - const onSubmit = edited => { + const onSubmit = (edited: boolean) => { this.model.setEditKey(null); const pattern = patternInput.value.trim(); if (edited && pattern) { diff --git a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts index b0b5b30d98..58cce34632 100644 --- a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts @@ -88,7 +88,7 @@ interface ISerializedPreferencesEditorInput { // Register Preferences Editor Input Factory class PreferencesEditorInputFactory implements IEditorInputFactory { - serialize(editorInput: EditorInput): string | null { + serialize(editorInput: EditorInput): string | undefined { const input = editorInput; if (input.details && input.master) { @@ -113,10 +113,10 @@ class PreferencesEditorInputFactory implements IEditorInputFactory { } } - return null; + return undefined; } - deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | null { + deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | undefined { const deserialized: ISerializedPreferencesEditorInput = JSON.parse(serializedEditorInput); const registry = Registry.as(EditorInputExtensions.EditorInputFactories); @@ -132,7 +132,7 @@ class PreferencesEditorInputFactory implements IEditorInputFactory { } } - return null; + return undefined; } } diff --git a/src/vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts index 7a17fbb45c..69accb99c9 100644 --- a/src/vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts @@ -287,7 +287,7 @@ class RemoteSearchProvider implements ISearchProvider { const timestamp = Date.now(); const duration = timestamp - start; const remoteSettings: IRemoteSetting[] = (result.value || []) - .map(r => { + .map((r: any) => { const key = JSON.parse(r.setting || r.Setting); const packageId = r['packageid']; const id = getSettingKey(key, packageId); @@ -447,7 +447,7 @@ class SettingMatches { readonly matches: IRange[]; - constructor(searchString: string, setting: ISetting, private requireFullQueryMatch: boolean, private searchDescription, private valuesMatcher: (filter: string, setting: ISetting) => IRange[]) { + constructor(searchString: string, setting: ISetting, private requireFullQueryMatch: boolean, private searchDescription: boolean, private valuesMatcher: (filter: string, setting: ISetting) => IRange[]) { this.matches = distinct(this._findMatchesInSetting(searchString, setting), (match) => `${match.startLineNumber}_${match.startColumn}_${match.endLineNumber}_${match.endColumn}_`); } diff --git a/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts index a50a199ccb..4adb3abd2f 100644 --- a/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts @@ -11,7 +11,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import * as collections from 'vs/base/common/collections'; import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; import { Iterator } from 'vs/base/common/iterator'; -import { isArray } from 'vs/base/common/types'; +import { isArray, withNullAsUndefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/settingsEditor2'; import { localize } from 'vs/nls'; @@ -573,7 +573,7 @@ export class SettingsEditor2 extends BaseEditor { this.tocTree.setSelection(element ? [element] : []); if (this.searchResultModel) { if (this.viewState.filterToCategory !== element) { - this.viewState.filterToCategory = element || undefined; + this.viewState.filterToCategory = withNullAsUndefined(element); this.renderTree(); this.settingsTree.scrollTop = 0; } diff --git a/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts b/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts index df1511593b..9a7f61760c 100644 --- a/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts @@ -74,8 +74,8 @@ export class GotoLineAction extends QuickOpenAction { } class GotoLineEntry extends EditorQuickOpenEntry { - private line: number; - private column: number; + private line!: number; + private column!: number; private handler: GotoLineHandler; constructor(line: string, editorService: IEditorService, handler: GotoLineHandler) { @@ -140,7 +140,7 @@ class GotoLineEntry extends EditorQuickOpenEntry { } getInput(): IEditorInput | null { - return this.editorService.activeEditor || null; + return types.withUndefinedAsNull(this.editorService.activeEditor); } getOptions(pinned?: boolean): ITextEditorOptions { @@ -218,8 +218,8 @@ export class GotoLineHandler extends QuickOpenHandler { static readonly ID = 'workbench.picker.line'; - private rangeHighlightDecorationId: IEditorLineDecoration | null; - private lastKnownEditorViewState: IEditorViewState | null; + private rangeHighlightDecorationId: IEditorLineDecoration | null = null; + private lastKnownEditorViewState: IEditorViewState | null = null; constructor(@IEditorService private readonly editorService: IEditorService) { super(); diff --git a/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts index 6acf231624..0a8ec572c0 100644 --- a/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts @@ -289,7 +289,7 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { } getInput(): IEditorInput | null { - return this.editorService.activeEditor || null; + return types.withUndefinedAsNull(this.editorService.activeEditor); } getOptions(pinned?: boolean): ITextEditorOptions { diff --git a/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts b/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts index 91206ce74c..d62ccfd29b 100644 --- a/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts @@ -137,7 +137,7 @@ export class ViewPickerHandler extends QuickOpenHandler { const result: ViewEntry[] = []; if (views.length) { for (const view of views) { - if (this.contextKeyService.contextMatchesRules(view.when || null)) { + if (this.contextKeyService.contextMatchesRules(view.when)) { result.push(new ViewEntry(view.name, viewlet.name, () => this.viewsService.openView(view.id, true))); } } diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 0b5c5dcc97..efe92d0eba 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -294,9 +294,9 @@ class DirtyDiffWidget extends PeekViewWidget { }; } - getActionItem(action: IAction): IActionItem | null { + getActionItem(action: IAction): IActionItem | undefined { if (!(action instanceof MenuItemAction)) { - return null; + return undefined; } return new DiffMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts index 20eb47e0c4..c871c66bf0 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts @@ -918,9 +918,9 @@ export class RepositoryPanel extends ViewletPanel { return this.menus.getTitleSecondaryActions(); } - getActionItem(action: IAction): IActionItem | null { + getActionItem(action: IAction): IActionItem | undefined { if (!(action instanceof MenuItemAction)) { - return null; + return undefined; } return new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); @@ -1221,9 +1221,9 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel { } } - getActionItem(action: IAction): IActionItem | null { + getActionItem(action: IAction): IActionItem | undefined { if (!(action instanceof MenuItemAction)) { - return null; + return undefined; } return new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); diff --git a/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts b/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts index 86496ea8cc..6e64bd2275 100644 --- a/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts +++ b/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts @@ -25,6 +25,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Schemas } from 'vs/base/common/network'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { withUndefinedAsNull } from 'vs/base/common/types'; class SymbolEntry extends EditorQuickOpenEntry { private bearingResolve: Promise; @@ -58,7 +59,7 @@ class SymbolEntry extends EditorQuickOpenEntry { return this.labelService.getUriLabel(this.bearing.location.uri, { relative: true }); } - return containerName || null; + return withUndefinedAsNull(containerName); } getIcon(): string { diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index a2d0afd756..96b7018e6c 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -26,6 +26,7 @@ import { overviewRulerFindMatchForeground } from 'vs/platform/theme/common/color import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; import { editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; +import { withNullAsUndefined } from 'vs/base/common/types'; export class Match { @@ -440,7 +441,7 @@ export class BaseFolderMatch extends Disposable { } name(): string { - return getBaseLabel(this.resource() || undefined) || ''; + return getBaseLabel(withNullAsUndefined(this.resource())) || ''; } parent(): SearchResult { diff --git a/src/vs/workbench/contrib/stats/node/workspaceStats.ts b/src/vs/workbench/contrib/stats/node/workspaceStats.ts index 9fd76f8501..01ae5dc865 100644 --- a/src/vs/workbench/contrib/stats/node/workspaceStats.ts +++ b/src/vs/workbench/contrib/stats/node/workspaceStats.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import * as crypto from 'crypto'; import { onUnexpectedError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { IFileService, IFileStat, IResolveFileResult } from 'vs/platform/files/common/files'; +import { IFileService, IFileStat, IResolveFileResult, IContent } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -196,12 +196,15 @@ export function getHashedRemotesFromConfig(text: string, stripEndingDotGit: bool export function getHashedRemotesFromUri(workspaceUri: URI, fileService: IFileService, stripEndingDotGit: boolean = false): Promise { const path = workspaceUri.path; const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` }); - return fileService.resolveFile(uri).then(() => { + return fileService.existsFile(uri).then(exists => { + if (!exists) { + return []; + } return fileService.resolveContent(uri, { acceptTextOnly: true }).then( content => getHashedRemotesFromConfig(content.value, stripEndingDotGit), err => [] // ignore missing or binary file ); - }, err => []); + }); } export class WorkspaceStats implements IWorkbenchContribution { @@ -431,10 +434,14 @@ export class WorkspaceStats implements IWorkbenchContribution { tags['workspace.android.cpp'] = true; } - function getFilePromises(filename, fileService, contentHandler): Promise[] { + function getFilePromises(filename: string, fileService: IFileService, contentHandler: (content: IContent) => void): Promise[] { return !nameSet.has(filename) ? [] : (folders as URI[]).map(workspaceUri => { const uri = workspaceUri.with({ path: `${workspaceUri.path !== '/' ? workspaceUri.path : ''}/${filename}` }); - return fileService.resolveFile(uri).then(() => { + return fileService.existsFile(uri).then(exists => { + if (!exists) { + return undefined; + } + return fileService.resolveContent(uri, { acceptTextOnly: true }).then(contentHandler); }, err => { // Ignore missing file @@ -613,12 +620,15 @@ export class WorkspaceStats implements IWorkbenchContribution { Promise.all(workspaceUris.map(workspaceUri => { const path = workspaceUri.path; const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` }); - return this.fileService.resolveFile(uri).then(() => { + return this.fileService.existsFile(uri).then(exists => { + if (!exists) { + return []; + } return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then( content => getDomainsOfRemotes(content.value, SecondLevelDomainWhitelist), err => [] // ignore missing or binary file ); - }, err => []); + }); })).then(domains => { const set = domains.reduce((set, list) => list.reduce((set, item) => set.add(item), set), new Set()); const list: string[] = []; @@ -679,12 +689,15 @@ export class WorkspaceStats implements IWorkbenchContribution { return Promise.all(workspaceUris.map(workspaceUri => { const path = workspaceUri.path; const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/pom.xml` }); - return this.fileService.resolveFile(uri).then(stats => { + return this.fileService.existsFile(uri).then(exists => { + if (!exists) { + return false; + } return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then( content => !!content.value.match(/azure/i), err => false ); - }, err => false); + }); })).then(javas => { if (javas.indexOf(true) !== -1) { tags['java'] = true; diff --git a/src/vs/workbench/contrib/tasks/common/problemMatcher.ts b/src/vs/workbench/contrib/tasks/common/problemMatcher.ts index a88507fa2e..5256ae1698 100644 --- a/src/vs/workbench/contrib/tasks/common/problemMatcher.ts +++ b/src/vs/workbench/contrib/tasks/common/problemMatcher.ts @@ -841,7 +841,9 @@ export class ProblemPatternParser extends Parser { private createSingleProblemPattern(value: Config.CheckedProblemPattern): ProblemPattern | null { let result = this.doCreateSingleProblemPattern(value, true); - if (result.kind === undefined) { + if (result === undefined) { + return null; + } else if (result.kind === undefined) { result.kind = ProblemLocationKind.Location; } return this.validateProblemPattern([result]) ? result : null; @@ -864,6 +866,9 @@ export class ProblemPatternParser extends Parser { let result: MultiLineProblemPattern = []; for (let i = 0; i < values.length; i++) { let pattern = this.doCreateSingleProblemPattern(values[i], false); + if (pattern === undefined) { + return null; + } if (i < values.length - 1) { if (!Types.isUndefined(pattern.loop) && pattern.loop) { pattern.loop = false; @@ -878,10 +883,10 @@ export class ProblemPatternParser extends Parser { return this.validateProblemPattern(result) ? result : null; } - private doCreateSingleProblemPattern(value: Config.CheckedProblemPattern, setDefaults: boolean): ProblemPattern { + private doCreateSingleProblemPattern(value: Config.CheckedProblemPattern, setDefaults: boolean): ProblemPattern | undefined { const regexp = this.createRegularExpression(value.regexp); if (regexp === undefined) { - throw new Error('Invalid regular expression'); + return undefined; } let result: ProblemPattern = { regexp }; if (value.kind) { @@ -1680,7 +1685,7 @@ export interface IProblemMatcherRegistry { onReady(): Promise; get(name: string): NamedProblemMatcher; keys(): string[]; - readonly onMatcherChanged; + readonly onMatcherChanged: Event; } class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry { diff --git a/src/vs/workbench/contrib/tasks/node/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts similarity index 99% rename from src/vs/workbench/contrib/tasks/node/taskConfiguration.ts rename to src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index 6438f46c36..dd1fd246e6 100644 --- a/src/vs/workbench/contrib/tasks/node/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -18,11 +18,8 @@ import { } from 'vs/workbench/contrib/tasks/common/problemMatcher'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; - -import * as Tasks from '../common/tasks'; -import { TaskDefinitionRegistry } from '../common/taskDefinitionRegistry'; - -import { TaskDefinition } from 'vs/workbench/contrib/tasks/node/tasks'; +import * as Tasks from './tasks'; +import { TaskDefinitionRegistry } from './taskDefinitionRegistry'; import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; export const enum ShellQuoting { @@ -932,7 +929,7 @@ namespace CommandConfiguration { } function fromBase(this: void, config: BaseCommandConfiguationShape, context: ParseContext): Tasks.CommandConfiguration | undefined { - let name: Tasks.CommandString = ShellString.from(config.command)!; + let name: Tasks.CommandString | undefined = ShellString.from(config.command); let runtime: Tasks.RuntimeType; if (Types.isString(config.type)) { if (config.type === 'shell' || config.type === 'process') { @@ -947,7 +944,7 @@ namespace CommandConfiguration { } let result: Tasks.CommandConfiguration = { - name: name!, + name: name, runtime: runtime!, presentation: PresentationOptions.from(config, context)! }; @@ -1200,7 +1197,7 @@ namespace TaskDependency { if (Types.isString(external)) { return { workspaceFolder: context.workspaceFolder, task: external }; } else if (TaskIdentifier.is(external)) { - return { workspaceFolder: context.workspaceFolder, task: TaskDefinition.createTaskIdentifier(external as Tasks.TaskIdentifier, context.problemReporter) }; + return { workspaceFolder: context.workspaceFolder, task: Tasks.TaskDefinition.createTaskIdentifier(external as Tasks.TaskIdentifier, context.problemReporter) }; } else { return undefined; } @@ -1332,7 +1329,7 @@ namespace ConfiguringTask { )); return undefined; } - let taskIdentifier: Tasks.KeyedTaskIdentifier | undefined = TaskDefinition.createTaskIdentifier(identifier, context.problemReporter); + let taskIdentifier: Tasks.KeyedTaskIdentifier | undefined = Tasks.TaskDefinition.createTaskIdentifier(identifier, context.problemReporter); if (taskIdentifier === undefined) { context.problemReporter.error(nls.localize( 'ConfigurationParser.incorrectType', diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index 788ad9fae4..cd7ea5453d 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -74,7 +74,7 @@ export interface ITaskService { getRecentlyUsedTasks(): LinkedMap; createSorter(): TaskSorter; - needsFolderQualification(); + needsFolderQualification(): boolean; canCustomize(task: ContributedTask | CustomTask): boolean; customize(task: ContributedTask | CustomTask, properties?: {}, openConfig?: boolean): Promise; openConfig(task: CustomTask | undefined): Promise; diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 6a00daf5a9..4030718d6c 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -3,6 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import * as Types from 'vs/base/common/types'; import { IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import * as Objects from 'vs/base/common/objects'; @@ -12,6 +13,7 @@ import { ProblemMatcher } from 'vs/workbench/contrib/tasks/common/problemMatcher import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry'; export const TASK_RUNNING_STATE = new RawContextKey('taskRunning', false); @@ -341,7 +343,7 @@ export interface TaskSourceConfigElement { } interface BaseTaskSource { - readonly kind; + readonly kind: string; readonly label: string; } @@ -467,7 +469,7 @@ export abstract class CommonTask { */ _label: string; - type; + type?: string; runOptions: RunOptions; @@ -477,7 +479,7 @@ export abstract class CommonTask { private _taskLoadMessages: string[] | undefined; - protected constructor(id: string, label: string | undefined, type, runOptions: RunOptions, + protected constructor(id: string, label: string | undefined, type: string | undefined, runOptions: RunOptions, configurationProperties: ConfigurationProperties, source: BaseTaskSource) { this._id = id; if (label) { @@ -575,7 +577,7 @@ export class CustomTask extends CommonTask { */ command: CommandConfiguration; - public constructor(id: string, source: WorkspaceTaskSource, label: string, type, command: CommandConfiguration | undefined, + public constructor(id: string, source: WorkspaceTaskSource, label: string, type: string, command: CommandConfiguration | undefined, hasDefinedMatchers: boolean, runOptions: RunOptions, configurationProperties: ConfigurationProperties) { super(id, label, undefined, runOptions, configurationProperties, source); this._source = source; @@ -677,7 +679,7 @@ export class ConfiguringTask extends CommonTask { configures: KeyedTaskIdentifier; - public constructor(id: string, source: WorkspaceTaskSource, label: string | undefined, type, + public constructor(id: string, source: WorkspaceTaskSource, label: string | undefined, type: string | undefined, configures: KeyedTaskIdentifier, runOptions: RunOptions, configurationProperties: ConfigurationProperties) { super(id, label, type, runOptions, configurationProperties, source); this._source = source; @@ -710,7 +712,7 @@ export class ContributedTask extends CommonTask { */ command: CommandConfiguration; - public constructor(id: string, source: ExtensionTaskSource, label: string, type, defines: KeyedTaskIdentifier, + public constructor(id: string, source: ExtensionTaskSource, label: string, type: string | undefined, defines: KeyedTaskIdentifier, command: CommandConfiguration, hasDefinedMatchers: boolean, runOptions: RunOptions, configurationProperties: ConfigurationProperties) { super(id, label, type, runOptions, configurationProperties, source); @@ -770,7 +772,7 @@ export class InMemoryTask extends CommonTask { type: 'inMemory'; - public constructor(id: string, source: InMemoryTaskSource, label: string, type, + public constructor(id: string, source: InMemoryTaskSource, label: string, type: string, runOptions: RunOptions, configurationProperties: ConfigurationProperties) { super(id, label, type, runOptions, configurationProperties, source); this._source = source; @@ -921,4 +923,76 @@ export namespace TaskEvent { return Object.freeze({ kind: TaskEventKind.Changed }); } } -} \ No newline at end of file +} + +export namespace KeyedTaskIdentifier { + function sortedStringify(literal: any): string { + const keys = Object.keys(literal).sort(); + let result: string = ''; + for (let position in keys) { + let stringified = literal[keys[position]]; + if (stringified instanceof Object) { + stringified = sortedStringify(test); + } else if (typeof stringified === 'string') { + stringified = stringified.replace(/,/g, ',,'); + } + result += keys[position] + ',' + stringified + ','; + } + return result; + } + export function create(value: TaskIdentifier): KeyedTaskIdentifier { + const resultKey = sortedStringify(value); + console.log(resultKey); + return { _key: resultKey, type: value.taskType }; + } +} + +export namespace TaskDefinition { + export function createTaskIdentifier(external: TaskIdentifier, reporter: { error(message: string): void; }): KeyedTaskIdentifier | undefined { + let definition = TaskDefinitionRegistry.get(external.type); + if (definition === undefined) { + // We have no task definition so we can't sanitize the literal. Take it as is + let copy = Objects.deepClone(external); + delete copy._key; + return KeyedTaskIdentifier.create(copy); + } + + let literal: { type: string;[name: string]: any } = Object.create(null); + literal.type = definition.taskType; + let required: Set = new Set(); + definition.required.forEach(element => required.add(element)); + + let properties = definition.properties; + for (let property of Object.keys(properties)) { + let value = external[property]; + if (value !== undefined && value !== null) { + literal[property] = value; + } else if (required.has(property)) { + let schema = properties[property]; + if (schema.default !== undefined) { + literal[property] = Objects.deepClone(schema.default); + } else { + switch (schema.type) { + case 'boolean': + literal[property] = false; + break; + case 'number': + case 'integer': + literal[property] = 0; + break; + case 'string': + literal[property] = ''; + break; + default: + reporter.error(nls.localize( + 'TaskDefinition.missingRequiredProperty', + 'Error: the task identifier \'{0}\' is missing the required property \'{1}\'. The task identifier will be ignored.', JSON.stringify(external, undefined, 0), property + )); + return undefined; + } + } + } + } + return KeyedTaskIdentifier.create(literal); + } +} diff --git a/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts index d9320a61a2..6e263f6364 100644 --- a/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts @@ -73,14 +73,13 @@ import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, T import { Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskEvent, TaskEventKind, TaskSet, TaskGroup, GroupType, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, - TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE, TaskRunSource + TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE, TaskRunSource, + KeyedTaskIdentifier as NKeyedTaskIdentifier, TaskDefinition } from 'vs/workbench/contrib/tasks/common/tasks'; import { ITaskService, ITaskProvider, ProblemMatcherRunOptions, CustomizationProperties, TaskFilter, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService'; import { getTemplates as getTaskTemplates } from 'vs/workbench/contrib/tasks/common/taskTemplates'; -import { KeyedTaskIdentifier as NKeyedTaskIdentifier, TaskDefinition } from 'vs/workbench/contrib/tasks/node/tasks'; - -import * as TaskConfig from '../node/taskConfiguration'; +import * as TaskConfig from '../common/taskConfiguration'; import { ProcessTaskSystem } from 'vs/workbench/contrib/tasks/node/processTaskSystem'; import { TerminalTaskSystem } from './terminalTaskSystem'; import { ProcessRunnerDetector } from 'vs/workbench/contrib/tasks/node/processRunnerDetector'; @@ -156,9 +155,9 @@ class BuildStatusBarItem extends Themable implements IStatusbarItem { const info = document.createElement('div'); const building = document.createElement('div'); - const errorTitle = n => nls.localize('totalErrors', "{0} Errors", n); - const warningTitle = n => nls.localize('totalWarnings', "{0} Warnings", n); - const infoTitle = n => nls.localize('totalInfos', "{0} Infos", n); + const errorTitle = (n: number) => nls.localize('totalErrors', "{0} Errors", n); + const warningTitle = (n: number) => nls.localize('totalWarnings', "{0} Warnings", n); + const infoTitle = (n: number) => nls.localize('totalInfos', "{0} Infos", n); Dom.addClass(element, 'task-statusbar-item'); element.title = nls.localize('problems', "Problems"); @@ -211,7 +210,7 @@ class BuildStatusBarItem extends Themable implements IStatusbarItem { })); const manyProblems = nls.localize('manyProblems', "10K+"); - const packNumber = n => n > 9999 ? manyProblems : n > 999 ? n.toString().charAt(0) + 'K' : n.toString(); + const packNumber = (n: number) => n > 9999 ? manyProblems : n > 999 ? n.toString().charAt(0) + 'K' : n.toString(); let updateLabel = (stats: MarkerStatistics) => { error.innerHTML = packNumber(stats.errors); error.title = errorIcon.title = errorTitle(stats.errors); diff --git a/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts index c4aebe8612..e38dc5a277 100644 --- a/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts @@ -1097,7 +1097,7 @@ export class TerminalTaskSystem implements ITaskSystem { } private collectTaskVariables(variables: Set, task: CustomTask | ContributedTask): void { - if (task.command) { + if (task.command && task.command.name) { this.collectCommandVariables(variables, task.command, task); } this.collectMatcherVariables(variables, task.configurationProperties.problemMatchers); diff --git a/src/vs/workbench/contrib/tasks/node/processRunnerDetector.ts b/src/vs/workbench/contrib/tasks/node/processRunnerDetector.ts index 73f9ef9bea..e060b887c6 100644 --- a/src/vs/workbench/contrib/tasks/node/processRunnerDetector.ts +++ b/src/vs/workbench/contrib/tasks/node/processRunnerDetector.ts @@ -14,7 +14,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import * as Tasks from '../common/tasks'; -import * as TaskConfig from './taskConfiguration'; +import * as TaskConfig from '../common/taskConfiguration'; const build = 'build'; const test = 'test'; diff --git a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts index 82454068a6..5712f2ad99 100644 --- a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts @@ -319,7 +319,7 @@ export class ProcessTaskSystem implements ITaskSystem { this.activeTask = task; const inactiveEvent = TaskEvent.create(TaskEventKind.Inactive, task); let processStartedSignaled: boolean = false; - const onProgress = (progress) => { + const onProgress = (progress: LineData) => { let line = Strings.removeAnsiEscapeCodes(progress.line); this.appendOutput(line + '\n'); startStopProblemMatcher.processLine(line); diff --git a/src/vs/workbench/contrib/tasks/node/tasks.ts b/src/vs/workbench/contrib/tasks/node/tasks.ts deleted file mode 100644 index e8e8d9f742..0000000000 --- a/src/vs/workbench/contrib/tasks/node/tasks.ts +++ /dev/null @@ -1,75 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; - -import * as crypto from 'crypto'; - -import * as Objects from 'vs/base/common/objects'; - -import { TaskIdentifier, KeyedTaskIdentifier, TaskDefinition } from 'vs/workbench/contrib/tasks/common/tasks'; -import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry'; - -namespace KeyedTaskIdentifier { - export function create(value: TaskIdentifier): KeyedTaskIdentifier { - const hash = crypto.createHash('md5'); - hash.update(JSON.stringify(value)); - let result = { _key: hash.digest('hex'), type: value.taskType }; - Objects.assign(result, value); - return result; - } -} - -namespace TaskDefinition { - export function createTaskIdentifier(external: TaskIdentifier, reporter: { error(message: string): void; }): KeyedTaskIdentifier | undefined { - let definition = TaskDefinitionRegistry.get(external.type); - if (definition === undefined) { - // We have no task definition so we can't sanitize the literal. Take it as is - let copy = Objects.deepClone(external); - delete copy._key; - return KeyedTaskIdentifier.create(copy); - } - - let literal: { type: string;[name: string]: any } = Object.create(null); - literal.type = definition.taskType; - let required: Set = new Set(); - definition.required.forEach(element => required.add(element)); - - let properties = definition.properties; - for (let property of Object.keys(properties)) { - let value = external[property]; - if (value !== undefined && value !== null) { - literal[property] = value; - } else if (required.has(property)) { - let schema = properties[property]; - if (schema.default !== undefined) { - literal[property] = Objects.deepClone(schema.default); - } else { - switch (schema.type) { - case 'boolean': - literal[property] = false; - break; - case 'number': - case 'integer': - literal[property] = 0; - break; - case 'string': - literal[property] = ''; - break; - default: - reporter.error(nls.localize( - 'TaskDefinition.missingRequiredProperty', - 'Error: the task identifier \'{0}\' is missing the required property \'{1}\'. The task identifier will be ignored.', JSON.stringify(external, undefined, 0), property - )); - return undefined; - } - } - } - } - return KeyedTaskIdentifier.create(literal); - } -} - -export { KeyedTaskIdentifier, TaskDefinition }; diff --git a/src/vs/workbench/contrib/tasks/test/electron-browser/configuration.test.ts b/src/vs/workbench/contrib/tasks/test/electron-browser/configuration.test.ts index 3375c204de..dec9c72a44 100644 --- a/src/vs/workbench/contrib/tasks/test/electron-browser/configuration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/electron-browser/configuration.test.ts @@ -13,7 +13,7 @@ import { ProblemMatcher, FileLocationKind, ProblemPattern, ApplyToKind } from 'v import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import * as Tasks from 'vs/workbench/contrib/tasks/common/tasks'; -import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration, CustomTask } from 'vs/workbench/contrib/tasks/node/taskConfiguration'; +import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration, CustomTask } from 'vs/workbench/contrib/tasks/common/taskConfiguration'; const workspaceFolder: IWorkspaceFolder = new WorkspaceFolder({ uri: URI.file('/workspace/folderOne'), diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 24ba122cd5..fc7b5d53e5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 92ad6accdb..6a152373f8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -34,6 +34,7 @@ import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; +import { withNullAsUndefined } from 'vs/base/common/types'; export const TERMINAL_PICKER_PREFIX = 'term '; @@ -291,7 +292,7 @@ export class SendSequenceTerminalCommand extends Command { const workspaceContextService = accessor.get(IWorkspaceContextService); const historyService = accessor.get(IHistoryService); const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.file); - const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) || undefined : undefined; + const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? withNullAsUndefined(workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; const resolvedText = configurationResolverService.resolve(lastActiveWorkspaceRoot, args.text); terminalInstance.sendText(resolvedText, false); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index cdb22ee824..f278b014f5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -894,9 +894,12 @@ export class TerminalInstance implements ITerminalInstance { if (platform.isWindows) { this._processManager.ptyProcessReady.then(() => { + if (this._processManager!.remoteAuthority) { + return; + } this._xtermReadyPromise.then(() => { if (!this._isDisposed) { - this._terminalInstanceService.createWindowsShellHelper(this._processManager!.shellProcessId, this, this._xterm); + this._windowsShellHelper = this._terminalInstanceService.createWindowsShellHelper(this._processManager!.shellProcessId, this, this._xterm); } }); }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 8b2e6156f6..ed936fbfd4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -161,7 +161,7 @@ export class TerminalPanel extends Panel { return this._contextMenuActions; } - public getActionItem(action: Action): IActionItem | null { + public getActionItem(action: Action): IActionItem | undefined { if (action.id === SwitchTerminalAction.ID) { return this._instantiationService.createInstance(SwitchTerminalActionItem, action); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index da5298a8c9..5f9a6a3f81 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -22,7 +22,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IProductService } from 'vs/platform/product/common/product'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IRemoteEnvironmentService } from 'vs/workbench/services/remote/common/remoteEnvironmentService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -77,7 +77,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { @IEnvironmentService private readonly _environmentService: IEnvironmentService, @IProductService private readonly _productService: IProductService, @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, - @IRemoteEnvironmentService private readonly _remoteEnvironmentService: IRemoteEnvironmentService + @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService ) { this.ptyProcessReady = new Promise(c => { this.onProcessReady(() => { @@ -123,7 +123,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { this.os = platform.OS; if (launchRemotely) { if (hasRemoteAuthority) { - this._remoteEnvironmentService.remoteEnvironment.then(env => { + this._remoteAgentService.getEnvironment().then(env => { if (!env) { return; } @@ -250,6 +250,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { } public async getLatency(): Promise { + await this.ptyProcessReady; if (!this._process) { return Promise.resolve(0); } diff --git a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts index acc0950d84..9ce8be5f70 100644 --- a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts @@ -18,7 +18,7 @@ function getMockTheme(type: ThemeType): ITheme { selector: '', label: '', type: type, - getColor: (colorId: ColorIdentifier) => themingRegistry.resolveDefaultColor(colorId, theme), + getColor: (colorId: ColorIdentifier): Color | undefined => themingRegistry.resolveDefaultColor(colorId, theme), defines: () => true }; return theme; diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 0ed27fc769..6908a4caab 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -53,7 +53,7 @@ export class SelectColorThemeAction extends Action { //configurationEntries(this.extensionGalleryService, localize('installColorThemes', "Install Additional Color Themes...")) ); - const selectTheme = (theme, applyTheme: boolean) => { + const selectTheme = (theme: IColorTheme, applyTheme: boolean) => { if (typeof theme.id === 'undefined') { // 'pick in marketplace' entry if (applyTheme) { openExtensionViewlet(this.viewletService, 'category:themes '); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditor.ts index 38a1e6c889..8af8048a82 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditor.ts @@ -245,6 +245,7 @@ export class WebviewEditor extends BaseEditor { webview.update(input.html, { allowScripts: input.options.enableScripts, localResourceRoots: input.options.localResourceRoots || this.getDefaultLocalResourceRoots(), + portMappings: input.options.portMapping, }, !!input.options.retainContextWhenHidden); if (this._webviewContent) { diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInput.ts index 88af525f84..5cb27ab80e 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInput.ts @@ -9,9 +9,8 @@ import { URI } from 'vs/base/common/uri'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { EditorInput, EditorModel, GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; -import * as vscode from 'vscode'; import { WebviewEvents, WebviewInputOptions } from './webviewEditorService'; -import { WebviewElement } from './webviewElement'; +import { WebviewElement, WebviewOptions } from './webviewElement'; export class WebviewEditorInput extends EditorInput { private static handlePool = 0; @@ -195,7 +194,7 @@ export class WebviewEditorInput extends EditorInput { return this._options; } - public setOptions(value: vscode.WebviewOptions) { + public setOptions(value: WebviewOptions) { this._options = { ...this._options, ...value @@ -204,7 +203,8 @@ export class WebviewEditorInput extends EditorInput { if (this._webview) { this._webview.options = { allowScripts: this._options.enableScripts, - localResourceRoots: this._options.localResourceRoots + localResourceRoots: this._options.localResourceRoots, + portMappings: this._options.portMapping, }; } } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInputFactory.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInputFactory.ts index 61e39dd74a..3b844e0539 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInputFactory.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInputFactory.ts @@ -35,9 +35,9 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { public serialize( input: WebviewEditorInput - ): string | null { + ): string | undefined { if (!this._webviewService.shouldPersist(input)) { - return null; + return undefined; } const data: SerializedWebview = { @@ -54,7 +54,7 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { try { return JSON.stringify(data); } catch { - return null; + return undefined; } } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts index ff7e4464a1..bf9edd5f51 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts @@ -11,8 +11,8 @@ import { createDecorator, IInstantiationService } from 'vs/platform/instantiatio import { GroupIdentifier } from 'vs/workbench/common/editor'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; -import * as vscode from 'vscode'; import { RevivedWebviewEditorInput, WebviewEditorInput } from './webviewEditorInput'; +import { IWebviewOptions, IWebviewPanelOptions } from 'vs/editor/common/modes'; export const IWebviewEditorService = createDecorator('webviewEditorService'); @@ -72,10 +72,10 @@ export interface WebviewReviver { export interface WebviewEvents { onMessage?(message: any): void; onDispose?(): void; - onDidClickLink?(link: URI, options: vscode.WebviewOptions): void; + onDidClickLink?(link: URI, options: IWebviewOptions): void; } -export interface WebviewInputOptions extends vscode.WebviewOptions, vscode.WebviewPanelOptions { +export interface WebviewInputOptions extends IWebviewOptions, IWebviewPanelOptions { tryRestoreScrollPosition?: boolean; } @@ -85,7 +85,8 @@ export function areWebviewInputOptionsEqual(a: WebviewInputOptions, b: WebviewIn && a.enableScripts === b.enableScripts && a.retainContextWhenHidden === b.retainContextWhenHidden && a.tryRestoreScrollPosition === b.tryRestoreScrollPosition - && (a.localResourceRoots === b.localResourceRoots || (Array.isArray(a.localResourceRoots) && Array.isArray(b.localResourceRoots) && equals(a.localResourceRoots, b.localResourceRoots, (a, b) => a.toString() === b.toString()))); + && (a.localResourceRoots === b.localResourceRoots || (Array.isArray(a.localResourceRoots) && Array.isArray(b.localResourceRoots) && equals(a.localResourceRoots, b.localResourceRoots, (a, b) => a.toString() === b.toString()))) + && (a.portMapping === b.portMapping || (Array.isArray(a.portMapping) && Array.isArray(b.portMapping) && equals(a.portMapping, b.portMapping, (a, b) => a.from === b.from && a.to === b.to))); } function canRevive(reviver: WebviewReviver, webview: WebviewEditorInput): boolean { @@ -128,11 +129,11 @@ export class WebviewEditorService implements IWebviewEditorService { viewType: string, title: string, showOptions: ICreateWebViewShowOptions, - options: vscode.WebviewOptions, + options: IWebviewOptions, extensionLocation: URI | undefined, events: WebviewEvents ): WebviewEditorInput { - const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, viewType, undefined, title, options, {}, events, extensionLocation, undefined); + const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, viewType, undefined, title, options, {}, events, extensionLocation); this._editorService.openEditor(webviewInput, { pinned: true, preserveFocus: showOptions.preserveFocus }, showOptions.group); return webviewInput; } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 470d73ce8a..5cee5c015c 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -3,9 +3,13 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { OnBeforeRequestDetails, OnHeadersReceivedDetails, Response } from 'electron'; import { addClass, addDisposableListener } from 'vs/base/browser/dom'; import { Emitter, Event } from 'vs/base/common/event'; +import { once } from 'vs/base/common/functional'; import { Disposable } from 'vs/base/common/lifecycle'; +import { isMacintosh } from 'vs/base/common/platform'; +import { endsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; @@ -15,8 +19,16 @@ import { DARK, ITheme, IThemeService, LIGHT } from 'vs/platform/theme/common/the import { registerFileProtocol, WebviewProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols'; import { areWebviewInputOptionsEqual } from './webviewEditorService'; import { WebviewFindWidget } from './webviewFindWidget'; -import { endsWith } from 'vs/base/common/strings'; -import { isMacintosh } from 'vs/base/common/platform'; + +export interface WebviewPortMapping { + readonly port: number; + readonly resolvedPort: number; +} + +export interface WebviewPortMapping { + readonly port: number; + readonly resolvedPort: number; +} export interface WebviewOptions { readonly allowSvgs?: boolean; @@ -28,6 +40,7 @@ export interface WebviewContentOptions { readonly allowScripts?: boolean; readonly svgWhiteList?: string[]; readonly localResourceRoots?: ReadonlyArray; + readonly portMappings?: ReadonlyArray; } interface IKeydownEvent { @@ -41,6 +54,58 @@ interface IKeydownEvent { repeat: boolean; } +type OnBeforeRequestDelegate = (details: OnBeforeRequestDetails) => Promise; +type OnHeadersReceivedDelegate = (details: OnHeadersReceivedDetails) => { cancel: boolean } | undefined; + +class WebviewSession extends Disposable { + + private readonly _onBeforeRequestDelegates: Array = []; + private readonly _onHeadersReceivedDelegates: Array = []; + + public constructor( + webview: Electron.WebviewTag, + ) { + super(); + + this._register(addDisposableListener(webview, 'did-start-loading', once(() => { + const contents = webview.getWebContents(); + if (!contents) { + return; + } + + contents.session.webRequest.onBeforeRequest(async (details, callback) => { + for (const delegate of this._onBeforeRequestDelegates) { + const result = await delegate(details); + if (typeof result !== 'undefined') { + callback(result); + return; + } + } + callback({}); + }); + + contents.session.webRequest.onHeadersReceived((details, callback) => { + for (const delegate of this._onHeadersReceivedDelegates) { + const result = delegate(details); + if (typeof result !== 'undefined') { + callback(result); + return; + } + } + callback({ cancel: false, responseHeaders: details.responseHeaders }); + }); + }))); + } + + public onBeforeRequest(delegate: OnBeforeRequestDelegate) { + this._onBeforeRequestDelegates.push(delegate); + } + + public onHeadersReceived(delegate: OnHeadersReceivedDelegate) { + this._onHeadersReceivedDelegates.push(delegate); + } +} + class WebviewProtocolProvider extends Disposable { constructor( webview: Electron.WebviewTag, @@ -51,21 +116,15 @@ class WebviewProtocolProvider extends Disposable { ) { super(); - let loaded = false; - this._register(addDisposableListener(webview, 'did-start-loading', () => { - if (loaded) { - return; - } - loaded = true; - + this._register(addDisposableListener(webview, 'did-start-loading', once(() => { const contents = webview.getWebContents(); if (contents) { - this.registerFileProtocols(contents); + this.registerProtocols(contents); } - })); + }))); } - private registerFileProtocols(contents: Electron.WebContents) { + private registerProtocols(contents: Electron.WebContents) { if (contents.isDestroyed()) { return; } @@ -82,52 +141,73 @@ class WebviewProtocolProvider extends Disposable { } } +class WebviewPortMappingProvider extends Disposable { + + constructor( + session: WebviewSession, + mappings: () => ReadonlyArray + ) { + super(); + + session.onBeforeRequest(async (details) => { + const uri = URI.parse(details.url); + if (uri.scheme !== 'http' && uri.scheme !== 'https') { + return undefined; + } + + const localhostMatch = /^localhost:(\d+)$/.exec(uri.authority); + if (localhostMatch) { + const port = +localhostMatch[1]; + for (const mapping of mappings()) { + if (mapping.port === port && mapping.port !== mapping.resolvedPort) { + return { + redirectURL: details.url.replace( + new RegExp(`^${uri.scheme}://localhost:${mapping.port}/`), + `${uri.scheme}://localhost:${mapping.resolvedPort}/`) + }; + } + } + } + + return undefined; + }); + } +} + class SvgBlocker extends Disposable { private readonly _onDidBlockSvg = this._register(new Emitter()); public readonly onDidBlockSvg = this._onDidBlockSvg.event; constructor( - webview: Electron.WebviewTag, + session: WebviewSession, private readonly _options: WebviewContentOptions, ) { super(); - let loaded = false; - this._register(addDisposableListener(webview, 'did-start-loading', () => { - if (loaded) { - return; - } - loaded = true; - - const contents = webview.getWebContents(); - if (!contents) { - return; + session.onBeforeRequest(async (details) => { + if (details.url.indexOf('.svg') > 0) { + const uri = URI.parse(details.url); + if (uri && !uri.scheme.match(/file/i) && endsWith(uri.path, '.svg') && !this.isAllowedSvg(uri)) { + this._onDidBlockSvg.fire(); + return { cancel: true }; + } } - contents.session.webRequest.onBeforeRequest((details, callback) => { - if (details.url.indexOf('.svg') > 0) { - const uri = URI.parse(details.url); - if (uri && !uri.scheme.match(/file/i) && endsWith(uri.path, '.svg') && !this.isAllowedSvg(uri)) { - this._onDidBlockSvg.fire(); - return callback({ cancel: true }); - } - } - return callback({}); - }); + return undefined; + }); - contents.session.webRequest.onHeadersReceived((details, callback) => { - const contentType: string[] = details.responseHeaders['content-type'] || details.responseHeaders['Content-Type']; - if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { - const uri = URI.parse(details.url); - if (uri && !this.isAllowedSvg(uri)) { - this._onDidBlockSvg.fire(); - return callback({ cancel: true }); - } + session.onHeadersReceived((details) => { + const contentType: string[] = details.responseHeaders['content-type'] || details.responseHeaders['Content-Type']; + if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { + const uri = URI.parse(details.url); + if (uri && !this.isAllowedSvg(uri)) { + this._onDidBlockSvg.fire(); + return { cancel: true }; } - return callback({ cancel: false, responseHeaders: details.responseHeaders }); - }); - })); + } + return undefined; + }); } private isAllowedSvg(uri: URI): boolean { @@ -236,7 +316,7 @@ export class WebviewElement extends Disposable { @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, @IEnvironmentService environmentService: IEnvironmentService, - @IFileService fileService: IFileService + @IFileService fileService: IFileService, ) { super(); this._webview = document.createElement('webview'); @@ -264,16 +344,23 @@ export class WebviewElement extends Disposable { })); }); - this._register( - new WebviewProtocolProvider( - this._webview, - this._options.extensionLocation, - () => (this._contentOptions.localResourceRoots || []), - environmentService, - fileService)); + const session = this._register(new WebviewSession(this._webview)); + + this._register(new WebviewProtocolProvider( + this._webview, + this._options.extensionLocation, + () => (this._contentOptions.localResourceRoots || []), + environmentService, + fileService)); + + this._register(new WebviewPortMappingProvider( + session, + () => (this._contentOptions.portMappings || []) + )); + if (!this._options.allowSvgs) { - const svgBlocker = this._register(new SvgBlocker(this._webview, this._contentOptions)); + const svgBlocker = this._register(new SvgBlocker(session, this._contentOptions)); svgBlocker.onDidBlockSvg(() => this.onDidBlockSvg()); } @@ -496,7 +583,7 @@ export class WebviewElement extends Disposable { if (!window || !window.webContents || window.webContents.isDestroyed()) { return; } - window.webContents.getZoomFactor(factor => { + window.webContents.getZoomFactor((factor: number) => { if (contents.isDestroyed()) { return; } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts index 80c69406b3..6cef4e4412 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts @@ -2,8 +2,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { extname, sep } from 'vs/base/common/path'; import { getMediaMime, MIME_UNKNOWN } from 'vs/base/common/mime'; +import { extname, sep } from 'vs/base/common/path'; import { startsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; @@ -11,7 +11,7 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; export const enum WebviewProtocol { CoreResource = 'vscode-core-resource', - VsCodeResource = 'vscode-resource' + VsCodeResource = 'vscode-resource', } function resolveContent(fileService: IFileService, resource: URI, mime: string, callback: any): void { @@ -60,7 +60,7 @@ export function registerFileProtocol( callback({ error: -10 /* ACCESS_DENIED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); }, (error) => { if (error) { - console.error('Failed to register protocol ' + protocol); + console.error(`Failed to register '${protocol}' protocol`); } }); } diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts index 956dd99c02..bb0e37b2bc 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts @@ -14,7 +14,7 @@ export class GettingStarted implements IWorkbenchContribution { private static readonly hideWelcomeSettingskey = 'workbench.hide.welcome'; - private welcomePageURL: string; + private welcomePageURL?: string; private appName: string; constructor( diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/test/common/gettingStarted.test.ts b/src/vs/workbench/contrib/welcome/gettingStarted/test/common/gettingStarted.test.ts deleted file mode 100644 index 2fa5afdd84..0000000000 --- a/src/vs/workbench/contrib/welcome/gettingStarted/test/common/gettingStarted.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IStorageService } from 'vs/platform/storage/common/storage'; - -suite('Workbench - GettingStarted', () => { - let instantiation: TestInstantiationService | null = null; - let hideWelcomeSettingsValue: string | null = null; - - suiteSetup(() => { - instantiation = new TestInstantiationService(); - instantiation.stub(IWorkspaceContextService, { - - }); - instantiation.stub(IStorageService, >{ - get: () => hideWelcomeSettingsValue, - store: (value) => hideWelcomeSettingsValue = value - }); - }); - - suiteTeardown(() => { - instantiation = null; - }); - - setup(() => { - hideWelcomeSettingsValue = null; - }); -}); \ No newline at end of file diff --git a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts index 1fde776d4f..2a38ce1db7 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts @@ -48,7 +48,7 @@ export class WalkThroughInput extends EditorInput { private disposables: IDisposable[] = []; - private promise: Promise | null; + private promise: Promise | null = null; private maxTopScroll = 0; private maxBottomScroll = 0; diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 9433b0d275..344766ed69 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -39,6 +39,10 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { Disposable } from 'vs/base/common/lifecycle'; import { registerWindowDriver } from 'vs/platform/driver/electron-browser/driver'; import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; class CodeRendererMain extends Disposable { @@ -168,6 +172,12 @@ class CodeRendererMain extends Disposable { const logService = this._register(this.createLogService(mainProcessService, environmentService)); serviceCollection.set(ILogService, logService); + // Remote + const remoteAuthorityResolverService = new RemoteAuthorityResolverService(); + serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService); + const remoteAgentService = new RemoteAgentService(this.configuration, environmentService, remoteAuthorityResolverService); + serviceCollection.set(IRemoteAgentService, remoteAgentService); + return this.resolveWorkspaceInitializationPayload(environmentService).then(payload => Promise.all([ this.createWorkspaceService(payload, environmentService, logService).then(service => { diff --git a/src/vs/workbench/services/broadcast/common/broadcast.ts b/src/vs/workbench/services/broadcast/common/broadcast.ts index fc9e13031b..a6a9151020 100644 --- a/src/vs/workbench/services/broadcast/common/broadcast.ts +++ b/src/vs/workbench/services/broadcast/common/broadcast.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts b/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts index 25c10a4e8d..0d1e0fdad2 100644 --- a/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts +++ b/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts @@ -31,7 +31,7 @@ export class BroadcastService extends Disposable implements IBroadcastService { } private registerListeners(): void { - ipc.on('vscode:broadcast', (event, b: IBroadcast) => { + ipc.on('vscode:broadcast', (event: unknown, b: IBroadcast) => { this.logService.trace(`Received broadcast from main in window ${this.windowId}: `, b); this._onBroadcast.fire(b); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index e17f45d51f..5c723ccaeb 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -27,6 +27,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { ITextModel } from 'vs/editor/common/model'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { withUndefinedAsNull, withNullAsUndefined } from 'vs/base/common/types'; export const enum ConfigurationEditingErrorCode { @@ -462,14 +463,14 @@ export class ConfigurationEditingService { // Check for prefix if (config.key === key) { const jsonPath = this.isWorkspaceConfigurationResource(resource) ? [key] : []; - return { key: jsonPath[jsonPath.length - 1], jsonPath, value: config.value, resource: resource || undefined, workspaceStandAloneConfigurationKey: key, target }; + return { key: jsonPath[jsonPath.length - 1], jsonPath, value: config.value, resource: withNullAsUndefined(resource), workspaceStandAloneConfigurationKey: key, target }; } // Check for prefix. const keyPrefix = `${key}.`; if (config.key.indexOf(keyPrefix) === 0) { const jsonPath = this.isWorkspaceConfigurationResource(resource) ? [key, config.key.substr(keyPrefix.length)] : [config.key.substr(keyPrefix.length)]; - return { key: jsonPath[jsonPath.length - 1], jsonPath, value: config.value, resource: resource || undefined, workspaceStandAloneConfigurationKey: key, target }; + return { key: jsonPath[jsonPath.length - 1], jsonPath, value: config.value, resource: withNullAsUndefined(resource), workspaceStandAloneConfigurationKey: key, target }; } } } @@ -484,7 +485,7 @@ export class ConfigurationEditingService { if (this.isWorkspaceConfigurationResource(resource)) { jsonPath = ['settings', ...jsonPath]; } - return { key, jsonPath, value: config.value, resource: resource || undefined, target }; + return { key, jsonPath, value: config.value, resource: withNullAsUndefined(resource), target }; } private isWorkspaceConfigurationResource(resource: URI | null): boolean { @@ -504,7 +505,7 @@ export class ConfigurationEditingService { if (target === ConfigurationTarget.WORKSPACE) { if (workbenchState === WorkbenchState.WORKSPACE) { - return workspace.configuration || null; + return withUndefinedAsNull(workspace.configuration); } if (workbenchState === WorkbenchState.FOLDER) { return workspace.folders[0].toResource(relativePath); diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index 8c6e92b9b6..664c297db8 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -40,7 +40,7 @@ import { IWorkspaceIdentifier } from 'vs/workbench/services/configuration/node/c class SettingsTestEnvironmentService extends EnvironmentService { - constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome) { + constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome: string) { super(args, _execPath); } diff --git a/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts index e21f605df9..12b978f019 100644 --- a/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts @@ -25,7 +25,6 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { ContextMenuService as HTMLContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; export class ContextMenuService extends Disposable implements IContextMenuService { @@ -42,14 +41,13 @@ export class ContextMenuService extends Disposable implements IContextMenuServic @IConfigurationService configurationService: IConfigurationService, @IEnvironmentService environmentService: IEnvironmentService, @IContextViewService contextViewService: IContextViewService, - @IThemeService themeService: IThemeService, - @ILayoutService layoutService: ILayoutService + @IThemeService themeService: IThemeService ) { super(); // Custom context menu: Linux/Windows if custom title is enabled if (!isMacintosh && getTitleBarStyle(configurationService, environmentService) === 'custom') { - this.impl = new HTMLContextMenuService(layoutService, telemetryService, notificationService, contextViewService, keybindingService, themeService); + this.impl = new HTMLContextMenuService(telemetryService, notificationService, contextViewService, keybindingService, themeService); } // Native context menu: otherwise diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 69e85a4da8..671201b8ce 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -21,7 +21,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { Schemas } from 'vs/base/common/network'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IRemoteEnvironmentService } from 'vs/workbench/services/remote/common/remoteEnvironmentService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; interface FileQuickPickItem extends IQuickPickItem { uri: URI; @@ -59,7 +59,7 @@ export class RemoteFileDialog { @IModelService private readonly modelService: IModelService, @IModeService private readonly modeService: IModeService, @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IRemoteEnvironmentService private readonly remoteEnvironmentService: IRemoteEnvironmentService, + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, ) { this.remoteAuthority = this.windowService.getConfiguration().remoteAuthority; @@ -111,7 +111,7 @@ export class RemoteFileDialog { private async getOptions(options: ISaveDialogOptions | IOpenDialogOptions): Promise { let defaultUri = options.defaultUri; if (!defaultUri) { - const env = await this.remoteEnvironmentService.remoteEnvironment; + const env = await this.remoteAgentService.getEnvironment(); if (env) { defaultUri = env.userHome; } else { diff --git a/src/vs/workbench/services/extensionManagement/node/multiExtensionManagement.ts b/src/vs/workbench/services/extensionManagement/node/multiExtensionManagement.ts index 8a7727d7ef..9a114e1155 100644 --- a/src/vs/workbench/services/extensionManagement/node/multiExtensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/node/multiExtensionManagement.ts @@ -14,12 +14,12 @@ import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localize } from 'vs/nls'; import { isUIExtension } from 'vs/workbench/services/extensions/node/extensionsUtil'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; export class MultiExtensionManagementService extends Disposable implements IExtensionManagementService { @@ -204,16 +204,10 @@ export class MultiExtensionManagementService extends Disposable implements IExte if (!this.extensionManagementServerService.remoteExtensionManagementServer) { return false; } - const connection = this.remoteAgentService.getConnection(); - if (!connection) { - return false; - } - - const remoteEnv = await connection.getEnvironment(); + const remoteEnv = await this.remoteAgentService.getEnvironment(); if (!remoteEnv) { return false; } - return remoteEnv.syncExtensions; } } diff --git a/src/vs/workbench/services/extensions/common/extensionDevOptions.ts b/src/vs/workbench/services/extensions/common/extensionDevOptions.ts index 29948328db..4daa0ac086 100644 --- a/src/vs/workbench/services/extensions/common/extensionDevOptions.ts +++ b/src/vs/workbench/services/extensions/common/extensionDevOptions.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Schemas } from 'vs/base/common/network'; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts index a1145f932e..c9bcedb1d1 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts @@ -8,6 +8,7 @@ import { TernarySearchTree } from 'vs/base/common/map'; import { realpathSync } from 'vs/base/node/extfs'; import { IExtensionHostProfile, IExtensionService, ProfileSegmentId, ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { withNullAsUndefined } from 'vs/base/common/types'; export class ExtensionHostProfiler { @@ -88,7 +89,7 @@ export class ExtensionHostProfiler { distilledIds.push(currSegmentId); distilledDeltas.push(currSegmentTime); } - currSegmentId = segmentId || undefined; + currSegmentId = withNullAsUndefined(segmentId); currSegmentTime = 0; } currSegmentTime += timeDeltas[i]; diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts index 0c1f9f3083..ab172ac8bc 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts @@ -23,7 +23,7 @@ import { exit, ExtensionHostMain } from 'vs/workbench/services/extensions/node/e const Module = require.__$__nodeRequire('module') as any; const originalLoad = Module._load; - Module._load = function (request) { + Module._load = function (request: string) { if (request === 'natives') { throw new Error('Either the extension or a NPM dependency is using the "natives" node module which is unsupported as it can cause a crash of the extension host. Click [here](https://go.microsoft.com/fwlink/?linkid=871887) to find out more'); } diff --git a/src/vs/workbench/services/extensions/node/lazyPromise.ts b/src/vs/workbench/services/extensions/node/lazyPromise.ts index 795c33bef9..827c5a0123 100644 --- a/src/vs/workbench/services/extensions/node/lazyPromise.ts +++ b/src/vs/workbench/services/extensions/node/lazyPromise.ts @@ -83,7 +83,7 @@ export class LazyPromise implements Promise { return this._ensureActual().then(undefined, error); } - public finally(callback): any { + public finally(callback: () => void): any { return this._ensureActual().finally(callback); } } diff --git a/src/vs/workbench/services/files/node/fileService.ts b/src/vs/workbench/services/files/node/fileService.ts index f4827cb9fe..8b52df3c78 100644 --- a/src/vs/workbench/services/files/node/fileService.ts +++ b/src/vs/workbench/services/files/node/fileService.ts @@ -8,7 +8,7 @@ import * as fs from 'fs'; import * as os from 'os'; import * as crypto from 'crypto'; import * as assert from 'assert'; -import { isParent, FileOperation, FileOperationEvent, IContent, IResolveFileOptions, IResolveFileResult, IResolveContentOptions, IFileStat, IStreamContent, FileOperationError, FileOperationResult, IUpdateContentOptions, FileChangeType, FileChangesEvent, ICreateFileOptions, IContentData, ITextSnapshot, IFilesConfiguration, IFileSystemProviderRegistrationEvent, IFileSystemProvider, ILegacyFileService } from 'vs/platform/files/common/files'; +import { isParent, FileOperation, FileOperationEvent, IContent, IResolveFileOptions, IResolveFileResult, IResolveContentOptions, IFileStat, IStreamContent, FileOperationError, FileOperationResult, IUpdateContentOptions, FileChangeType, FileChangesEvent, ICreateFileOptions, IContentData, ITextSnapshot, IFilesConfiguration, IFileSystemProviderRegistrationEvent, IFileSystemProvider, ILegacyFileService, IFileStatWithMetadata, IFileService, IResolveMetadataFileOptions } from 'vs/platform/files/common/files'; import { MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/platform/files/node/fileConstants'; import { isEqualOrParent } from 'vs/base/common/extpath'; import { ResourceMap } from 'vs/base/common/map'; @@ -42,13 +42,14 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import product from 'vs/platform/product/node/product'; import { IEncodingOverride, ResourceEncodings } from 'vs/workbench/services/files/node/encoding'; import { createReadableOfSnapshot } from 'vs/workbench/services/files/node/streams'; +import { withUndefinedAsNull } from 'vs/base/common/types'; export interface IFileServiceTestOptions { disableWatcher?: boolean; encodingOverride?: IEncodingOverride[]; } -export class FileService extends Disposable implements ILegacyFileService { +export class FileService extends Disposable implements ILegacyFileService, IFileService { _serviceBrand: any; @@ -228,6 +229,7 @@ export class FileService extends Disposable implements ILegacyFileService { etag: streamContent.etag, encoding: streamContent.encoding, isReadonly: streamContent.isReadonly, + size: streamContent.size, value: '' }; @@ -279,6 +281,7 @@ export class FileService extends Disposable implements ILegacyFileService { result.name = stat.name; result.mtime = stat.mtime; result.etag = stat.etag; + result.size = stat.size; // Return early if resource is a directory if (stat.isDirectory) { @@ -462,7 +465,7 @@ export class FileService extends Disposable implements ILegacyFileService { } }; - let currentPosition: number | null = (options && options.position) || null; + let currentPosition: number | null = withUndefinedAsNull(options && options.position); const readChunk = () => { fs.read(fd, chunkBuffer, 0, chunkBuffer.length, currentPosition, (err, bytesRead) => { @@ -531,7 +534,7 @@ export class FileService extends Disposable implements ILegacyFileService { }); } - updateContent(resource: uri, value: string | ITextSnapshot, options: IUpdateContentOptions = Object.create(null)): Promise { + updateContent(resource: uri, value: string | ITextSnapshot, options: IUpdateContentOptions = Object.create(null)): Promise { if (options.writeElevated) { return this.doUpdateContentElevated(resource, value, options); } @@ -539,7 +542,7 @@ export class FileService extends Disposable implements ILegacyFileService { return this.doUpdateContent(resource, value, options); } - private doUpdateContent(resource: uri, value: string | ITextSnapshot, options: IUpdateContentOptions = Object.create(null)): Promise { + private doUpdateContent(resource: uri, value: string | ITextSnapshot, options: IUpdateContentOptions = Object.create(null)): Promise { const absolutePath = this.toAbsolutePath(resource); // 1.) check file for writing @@ -650,7 +653,7 @@ export class FileService extends Disposable implements ILegacyFileService { }); } - private doUpdateContentElevated(resource: uri, value: string | ITextSnapshot, options: IUpdateContentOptions = Object.create(null)): Promise { + private doUpdateContentElevated(resource: uri, value: string | ITextSnapshot, options: IUpdateContentOptions = Object.create(null)): Promise { const absolutePath = this.toAbsolutePath(resource); // 1.) check file for writing @@ -712,7 +715,7 @@ export class FileService extends Disposable implements ILegacyFileService { }); } - createFile(resource: uri, content: string = '', options: ICreateFileOptions = Object.create(null)): Promise { + createFile(resource: uri, content: string = '', options: ICreateFileOptions = Object.create(null)): Promise { const absolutePath = this.toAbsolutePath(resource); let checkFilePromise: Promise; @@ -805,15 +808,15 @@ export class FileService extends Disposable implements ILegacyFileService { )); } - moveFile(source: uri, target: uri, overwrite?: boolean): Promise { + moveFile(source: uri, target: uri, overwrite?: boolean): Promise { return this.moveOrCopyFile(source, target, false, !!overwrite); } - copyFile(source: uri, target: uri, overwrite?: boolean): Promise { + copyFile(source: uri, target: uri, overwrite?: boolean): Promise { return this.moveOrCopyFile(source, target, true, !!overwrite); } - private moveOrCopyFile(source: uri, target: uri, keepCopy: boolean, overwrite: boolean): Promise { + private moveOrCopyFile(source: uri, target: uri, keepCopy: boolean, overwrite: boolean): Promise { const sourcePath = this.toAbsolutePath(source); const targetPath = this.toAbsolutePath(target); @@ -821,7 +824,7 @@ export class FileService extends Disposable implements ILegacyFileService { return this.doMoveOrCopyFile(sourcePath, targetPath, keepCopy, overwrite).then(() => { // 2.) resolve - return this.resolve(target).then(result => { + return this.resolve(target, { resolveMetadata: true }).then(result => { // Events (unless it was a no-op because paths are identical) if (sourcePath !== targetPath) { @@ -945,6 +948,8 @@ export class FileService extends Disposable implements ILegacyFileService { return paths.normalize(resource.fsPath); } + private resolve(resource: uri, options: IResolveMetadataFileOptions): Promise; + private resolve(resource: uri, options?: IResolveFileOptions): Promise; private resolve(resource: uri, options: IResolveFileOptions = Object.create(null)): Promise { return this.toStatResolver(resource).then(model => model.resolve(options)); } @@ -1083,6 +1088,8 @@ export class FileService extends Disposable implements ILegacyFileService { // Tests only + resolveFile(resource: uri, options?: IResolveFileOptions): Promise; + resolveFile(resource: uri, options: IResolveMetadataFileOptions): Promise; resolveFile(resource: uri, options?: IResolveFileOptions): Promise { return this.resolve(resource, options); } @@ -1092,14 +1099,14 @@ export class FileService extends Disposable implements ILegacyFileService { .then(stat => ({ stat, success: true }), error => ({ stat: undefined, success: false })))); } - createFolder(resource: uri): Promise { + createFolder(resource: uri): Promise { // 1.) Create folder const absolutePath = this.toAbsolutePath(resource); return pfs.mkdirp(absolutePath).then(() => { // 2.) Resolve - return this.resolve(resource).then(result => { + return this.resolve(resource, { resolveMetadata: true }).then(result => { // Events this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, result)); diff --git a/src/vs/workbench/services/files/node/remoteFileService.ts b/src/vs/workbench/services/files/node/remoteFileService.ts index 464c9536ec..142dbc11ce 100644 --- a/src/vs/workbench/services/files/node/remoteFileService.ts +++ b/src/vs/workbench/services/files/node/remoteFileService.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { flatten, isNonEmptyArray } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; @@ -14,7 +14,7 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FileWriteOptions, FileSystemProviderCapabilities, IContent, ICreateFileOptions, IFileStat, IFileSystemProvider, IFilesConfiguration, IResolveContentOptions, IResolveFileOptions, IResolveFileResult, IStat, IStreamContent, ITextSnapshot, IUpdateContentOptions, StringSnapshot, IWatchOptions, FileType, ILegacyFileService, IFileService, toFileOperationResult } from 'vs/platform/files/common/files'; +import { FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FileWriteOptions, FileSystemProviderCapabilities, IContent, ICreateFileOptions, IFileStat, IFileSystemProvider, IFilesConfiguration, IResolveContentOptions, IResolveFileOptions, IResolveFileResult, IStat, IStreamContent, ITextSnapshot, IUpdateContentOptions, StringSnapshot, IWatchOptions, FileType, ILegacyFileService, IFileService, toFileOperationResult, IFileStatWithMetadata, IResolveMetadataFileOptions, etag } from 'vs/platform/files/common/files'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -46,7 +46,7 @@ function toIFileStat(provider: IFileSystemProvider, tuple: [URI, IStat], recurse isReadonly: !!(provider.capabilities & FileSystemProviderCapabilities.Readonly), mtime: stat.mtime, size: stat.size, - etag: stat.mtime.toString(29) + stat.size.toString(31), + etag: etag(stat.mtime, stat.size), }; if (fileStat.isDirectory) { @@ -223,6 +223,8 @@ export class RemoteFileService extends FileService { }); } + resolveFile(resource: URI, options: IResolveMetadataFileOptions): Promise; + resolveFile(resource: URI, options?: IResolveFileOptions): Promise; resolveFile(resource: URI, options?: IResolveFileOptions): Promise { if (resource.scheme === Schemas.file) { return super.resolveFile(resource, options); @@ -240,30 +242,6 @@ export class RemoteFileService extends FileService { } } - resolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): Promise { - - // soft-groupBy, keep order, don't rearrange/merge groups - let groups: Array = []; - let group: typeof toResolve | undefined; - for (const request of toResolve) { - if (!group || group[0].resource.scheme !== request.resource.scheme) { - group = []; - groups.push(group); - } - group.push(request); - } - - const promises: Promise[] = []; - for (const group of groups) { - if (group[0].resource.scheme === Schemas.file) { - promises.push(super.resolveFiles(group)); - } else { - promises.push(this._doResolveFiles(group)); - } - } - return Promise.all(promises).then(data => flatten(data)); - } - private _doResolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): Promise { return this._withProvider(toResolve[0].resource).then(provider => { let result: IResolveFileResult[] = []; @@ -346,7 +324,8 @@ export class RemoteFileService extends FileService { name: fileStat.name, etag: fileStat.etag, mtime: fileStat.mtime, - isReadonly: fileStat.isReadonly + isReadonly: fileStat.isReadonly, + size: fileStat.size }; }); }); @@ -384,7 +363,7 @@ export class RemoteFileService extends FileService { return provider; } - createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise { + createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise { if (resource.scheme === Schemas.file) { return super.createFile(resource, content, options); } else { @@ -407,7 +386,7 @@ export class RemoteFileService extends FileService { } } - updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise { + updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise { if (resource.scheme === Schemas.file) { return super.updateContent(resource, value, options); } else { @@ -420,17 +399,17 @@ export class RemoteFileService extends FileService { } } - private _writeFile(provider: IFileSystemProvider, resource: URI, snapshot: ITextSnapshot, preferredEncoding: string | undefined = undefined, options: FileWriteOptions): Promise { + private _writeFile(provider: IFileSystemProvider, resource: URI, snapshot: ITextSnapshot, preferredEncoding: string | undefined = undefined, options: FileWriteOptions): Promise { const readable = createReadableOfSnapshot(snapshot); const { encoding, hasBOM } = this.encoding.getWriteEncoding(resource, preferredEncoding); const encoder = encodeStream(encoding, { addBOM: hasBOM }); const target = createWritableOfProvider(provider, resource, options); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { readable.pipe(encoder).pipe(target); target.once('error', err => reject(err)); target.once('finish', _ => resolve(undefined)); }).then(_ => { - return this.resolveFile(resource); + return this.resolveFile(resource, { resolveMetadata: true }) as Promise; }); } @@ -440,6 +419,7 @@ export class RemoteFileService extends FileService { value: '', encoding: content.encoding, etag: content.etag, + size: content.size, mtime: content.mtime, name: content.name, resource: content.resource, @@ -465,52 +445,7 @@ export class RemoteFileService extends FileService { } } - moveFile(source: URI, target: URI, overwrite?: boolean): Promise { - if (source.scheme !== target.scheme) { - return this._doMoveAcrossScheme(source, target); - } else if (source.scheme === Schemas.file) { - return super.moveFile(source, target, overwrite); - } else { - return this._doMoveWithInScheme(source, target, overwrite); - } - } - - private _doMoveWithInScheme(source: URI, target: URI, overwrite: boolean = false): Promise { - - const prepare = overwrite - ? Promise.resolve(this.del(target, { recursive: true }).catch(_err => { /*ignore*/ })) - : Promise.resolve(); - - return prepare.then(() => this._withProvider(source)).then(RemoteFileService._throwIfFileSystemIsReadonly).then(provider => { - return RemoteFileService._mkdirp(provider, resources.dirname(target)).then(() => { - return provider.rename(source, target, { overwrite }).then(() => { - return this.resolveFile(target); - }).then(fileStat => { - this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.MOVE, fileStat)); - return fileStat; - }, err => { - const result = toFileOperationResult(err); - if (result === FileOperationResult.FILE_MOVE_CONFLICT) { - throw new FileOperationError(localize('fileMoveConflict', "Unable to move/copy. File already exists at destination."), result); - } - throw err; - }); - }); - }); - } - - private _doMoveAcrossScheme(source: URI, target: URI, overwrite?: boolean): Promise { - return this.copyFile(source, target, overwrite).then(() => { - return this.del(source, { recursive: true }); - }).then(() => { - return this.resolveFile(target); - }).then(fileStat => { - this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.MOVE, fileStat)); - return fileStat; - }); - } - - copyFile(source: URI, target: URI, overwrite?: boolean): Promise { + copyFile(source: URI, target: URI, overwrite?: boolean): Promise { if (source.scheme === target.scheme && source.scheme === Schemas.file) { return super.copyFile(source, target, overwrite); } @@ -520,7 +455,7 @@ export class RemoteFileService extends FileService { if (source.scheme === target.scheme && (provider.capabilities & FileSystemProviderCapabilities.FileFolderCopy)) { // good: provider supports copy withing scheme return provider.copy!(source, target, { overwrite: !!overwrite }).then(() => { - return this.resolveFile(target); + return this.resolveFile(target, { resolveMetadata: true }); }).then(fileStat => { this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.COPY, fileStat)); return fileStat; diff --git a/src/vs/workbench/services/files/test/electron-browser/fileService.test.ts b/src/vs/workbench/services/files/test/electron-browser/fileService.test.ts index e7b839fec4..76ba4efebe 100644 --- a/src/vs/workbench/services/files/test/electron-browser/fileService.test.ts +++ b/src/vs/workbench/services/files/test/electron-browser/fileService.test.ts @@ -98,314 +98,6 @@ suite('FileService', () => { }); }); - test('renameFile', () => { - let event: FileOperationEvent; - const toDispose = service.onAfterOperation(e => { - event = e; - }); - - const resource = uri.file(path.join(testDir, 'index.html')); - return service.resolveFile(resource).then(source => { - return service.moveFile(source.resource, uri.file(path.join(path.dirname(source.resource.fsPath), 'other.html'))).then(renamed => { - assert.equal(fs.existsSync(renamed.resource.fsPath), true); - assert.equal(fs.existsSync(source.resource.fsPath), false); - - assert.ok(event); - assert.equal(event.resource.fsPath, resource.fsPath); - assert.equal(event.operation, FileOperation.MOVE); - assert.equal(event.target!.resource.fsPath, renamed.resource.fsPath); - toDispose.dispose(); - }); - }); - }); - - test('renameFile - multi folder', function () { - let event: FileOperationEvent; - const toDispose = service.onAfterOperation(e => { - event = e; - }); - - const multiFolderPaths = ['a', 'couple', 'of', 'folders']; - const renameToPath = path.join(...multiFolderPaths, 'other.html'); - - const resource = uri.file(path.join(testDir, 'index.html')); - return service.resolveFile(resource).then(source => { - return service.moveFile(source.resource, uri.file(path.join(path.dirname(source.resource.fsPath), renameToPath))).then(renamed => { - assert.equal(fs.existsSync(renamed.resource.fsPath), true); - assert.equal(fs.existsSync(source.resource.fsPath), false); - - assert.ok(event); - assert.equal(event.resource.fsPath, resource.fsPath); - assert.equal(event.operation, FileOperation.MOVE); - assert.equal(event.target!.resource.fsPath, renamed.resource.fsPath); - toDispose.dispose(); - }); - }); - }); - - test('renameFolder', () => { - let event: FileOperationEvent; - const toDispose = service.onAfterOperation(e => { - event = e; - }); - - const resource = uri.file(path.join(testDir, 'deep')); - return service.resolveFile(resource).then(source => { - return service.moveFile(source.resource, uri.file(path.join(path.dirname(source.resource.fsPath), 'deeper'))).then(renamed => { - assert.equal(fs.existsSync(renamed.resource.fsPath), true); - assert.equal(fs.existsSync(source.resource.fsPath), false); - - assert.ok(event); - assert.equal(event.resource.fsPath, resource.fsPath); - assert.equal(event.operation, FileOperation.MOVE); - assert.equal(event.target!.resource.fsPath, renamed.resource.fsPath); - toDispose.dispose(); - }); - }); - }); - - test('renameFolder - multi folder', function () { - let event: FileOperationEvent; - const toDispose = service.onAfterOperation(e => { - event = e; - }); - - const multiFolderPaths = ['a', 'couple', 'of', 'folders']; - const renameToPath = path.join(...multiFolderPaths); - - const resource = uri.file(path.join(testDir, 'deep')); - return service.resolveFile(resource).then(source => { - return service.moveFile(source.resource, uri.file(path.join(path.dirname(source.resource.fsPath), renameToPath))).then(renamed => { - assert.equal(fs.existsSync(renamed.resource.fsPath), true); - assert.equal(fs.existsSync(source.resource.fsPath), false); - - assert.ok(event); - assert.equal(event.resource.fsPath, resource.fsPath); - assert.equal(event.operation, FileOperation.MOVE); - assert.equal(event.target!.resource.fsPath, renamed.resource.fsPath); - toDispose.dispose(); - }); - }); - }); - test('renameFile - MIX CASE', function () { - let event: FileOperationEvent; - const toDispose = service.onAfterOperation(e => { - event = e; - }); - - const resource = uri.file(path.join(testDir, 'index.html')); - return service.resolveFile(resource).then(source => { - return service.moveFile(source.resource, uri.file(path.join(path.dirname(source.resource.fsPath), 'INDEX.html'))).then(renamed => { - assert.equal(fs.existsSync(renamed.resource.fsPath), true); - assert.equal(path.basename(renamed.resource.fsPath), 'INDEX.html'); - - assert.ok(event); - assert.equal(event.resource.fsPath, resource.fsPath); - assert.equal(event.operation, FileOperation.MOVE); - assert.equal(event.target!.resource.fsPath, renamed.resource.fsPath); - toDispose.dispose(); - }); - }); - }); - - test('moveFile', () => { - let event: FileOperationEvent; - const toDispose = service.onAfterOperation(e => { - event = e; - }); - - const resource = uri.file(path.join(testDir, 'index.html')); - return service.resolveFile(resource).then(source => { - return service.moveFile(source.resource, uri.file(path.join(testDir, 'other.html'))).then(renamed => { - assert.equal(fs.existsSync(renamed.resource.fsPath), true); - assert.equal(fs.existsSync(source.resource.fsPath), false); - - assert.ok(event); - assert.equal(event.resource.fsPath, resource.fsPath); - assert.equal(event.operation, FileOperation.MOVE); - assert.equal(event.target!.resource.fsPath, renamed.resource.fsPath); - toDispose.dispose(); - }); - }); - }); - - test('move - source parent of target', function () { - let event: FileOperationEvent; - const toDispose = service.onAfterOperation(e => { - event = e; - }); - - return service.resolveFile(uri.file(path.join(testDir, 'index.html'))).then(source => { - return service.moveFile(uri.file(testDir), uri.file(path.join(testDir, 'binary.txt'))).then(undefined, (e: Error) => { - assert.ok(e); - - assert.ok(!event); - toDispose.dispose(); - }); - }); - }); - - test('move - FILE_MOVE_CONFLICT', function () { - let event: FileOperationEvent; - const toDispose = service.onAfterOperation(e => { - event = e; - }); - - return service.resolveFile(uri.file(path.join(testDir, 'index.html'))).then(source => { - return service.moveFile(source.resource, uri.file(path.join(testDir, 'binary.txt'))).then(undefined, (e: FileOperationError) => { - assert.equal(e.fileOperationResult, FileOperationResult.FILE_MOVE_CONFLICT); - - assert.ok(!event); - toDispose.dispose(); - }); - }); - }); - - test('moveFile - MIX CASE', function () { - let event: FileOperationEvent; - const toDispose = service.onAfterOperation(e => { - event = e; - }); - - const resource = uri.file(path.join(testDir, 'index.html')); - return service.resolveFile(resource).then(source => { - return service.moveFile(source.resource, uri.file(path.join(testDir, 'INDEX.html'))).then(renamed => { - assert.equal(fs.existsSync(renamed.resource.fsPath), true); - assert.equal(path.basename(renamed.resource.fsPath), 'INDEX.html'); - - assert.ok(event); - assert.equal(event.resource.fsPath, resource.fsPath); - assert.equal(event.operation, FileOperation.MOVE); - assert.equal(event.target!.resource.fsPath, renamed.resource.fsPath); - toDispose.dispose(); - }); - }); - }); - - test('moveFile - overwrite folder with file', function () { - let createEvent: FileOperationEvent; - let moveEvent: FileOperationEvent; - let deleteEvent: FileOperationEvent; - const toDispose = service.onAfterOperation(e => { - if (e.operation === FileOperation.CREATE) { - createEvent = e; - } else if (e.operation === FileOperation.DELETE) { - deleteEvent = e; - } else if (e.operation === FileOperation.MOVE) { - moveEvent = e; - } - }); - - return service.resolveFile(uri.file(testDir)).then(parent => { - const folderResource = uri.file(path.join(parent.resource.fsPath, 'conway.js')); - return service.createFolder(folderResource).then(f => { - const resource = uri.file(path.join(testDir, 'deep', 'conway.js')); - return service.moveFile(resource, f.resource, true).then(moved => { - assert.equal(fs.existsSync(moved.resource.fsPath), true); - assert.ok(fs.statSync(moved.resource.fsPath).isFile); - - assert.ok(createEvent); - assert.ok(deleteEvent); - assert.ok(moveEvent); - - assert.equal(moveEvent.resource.fsPath, resource.fsPath); - assert.equal(moveEvent!.target!.resource.fsPath, moved.resource.fsPath); - - assert.equal(deleteEvent.resource.fsPath, folderResource.fsPath); - - toDispose.dispose(); - }); - }); - }); - }); - - test('copyFile', () => { - let event: FileOperationEvent; - const toDispose = service.onAfterOperation(e => { - event = e; - }); - - return service.resolveFile(uri.file(path.join(testDir, 'index.html'))).then(source => { - const resource = uri.file(path.join(testDir, 'other.html')); - return service.copyFile(source.resource, resource).then(copied => { - assert.equal(fs.existsSync(copied.resource.fsPath), true); - assert.equal(fs.existsSync(source.resource.fsPath), true); - - assert.ok(event); - assert.equal(event.resource.fsPath, source.resource.fsPath); - assert.equal(event.operation, FileOperation.COPY); - assert.equal(event.target!.resource.fsPath, copied.resource.fsPath); - toDispose.dispose(); - }); - }); - }); - - test('copyFile - overwrite folder with file', function () { - let createEvent: FileOperationEvent; - let copyEvent: FileOperationEvent; - let deleteEvent: FileOperationEvent; - const toDispose = service.onAfterOperation(e => { - if (e.operation === FileOperation.CREATE) { - createEvent = e; - } else if (e.operation === FileOperation.DELETE) { - deleteEvent = e; - } else if (e.operation === FileOperation.COPY) { - copyEvent = e; - } - }); - - return service.resolveFile(uri.file(testDir)).then(parent => { - const folderResource = uri.file(path.join(parent.resource.fsPath, 'conway.js')); - return service.createFolder(folderResource).then(f => { - const resource = uri.file(path.join(testDir, 'deep', 'conway.js')); - return service.copyFile(resource, f.resource, true).then(copied => { - assert.equal(fs.existsSync(copied.resource.fsPath), true); - assert.ok(fs.statSync(copied.resource.fsPath).isFile); - - assert.ok(createEvent); - assert.ok(deleteEvent); - assert.ok(copyEvent); - - assert.equal(copyEvent.resource.fsPath, resource.fsPath); - assert.equal(copyEvent.target!.resource.fsPath, copied.resource.fsPath); - - assert.equal(deleteEvent.resource.fsPath, folderResource.fsPath); - - toDispose.dispose(); - }); - }); - }); - }); - - test('copyFile - MIX CASE', function () { - return service.resolveFile(uri.file(path.join(testDir, 'index.html'))).then(source => { - return service.moveFile(source.resource, uri.file(path.join(path.dirname(source.resource.fsPath), 'CONWAY.js'))).then(renamed => { - assert.equal(fs.existsSync(renamed.resource.fsPath), true); - assert.ok(fs.readdirSync(testDir).some(f => f === 'CONWAY.js')); - - return service.resolveFile(uri.file(path.join(testDir, 'deep', 'conway.js'))).then(source => { - const targetParent = uri.file(testDir); - const target = targetParent.with({ path: path.posix.join(targetParent.path, path.posix.basename(source.resource.path)) }); - - return service.copyFile(source.resource, target, true).then(res => { // CONWAY.js => conway.js - assert.equal(fs.existsSync(res.resource.fsPath), true); - assert.ok(fs.readdirSync(testDir).some(f => f === 'conway.js')); - }); - }); - }); - }); - }); - - test('copyFile - same file', function () { - return service.resolveFile(uri.file(path.join(testDir, 'index.html'))).then(source => { - const targetParent = uri.file(path.dirname(source.resource.fsPath)); - const target = targetParent.with({ path: path.posix.join(targetParent.path, path.posix.basename(source.resource.path)) }); - return service.copyFile(source.resource, target, true).then(copied => { - assert.equal(copied.size, source.size); - }); - }); - }); - test('updateContent', () => { const resource = uri.file(path.join(testDir, 'small.txt')); diff --git a/src/vs/workbench/services/files2/common/fileService2.ts b/src/vs/workbench/services/files2/common/fileService2.ts index 6d69c7bdc4..7c2fc7697d 100644 --- a/src/vs/workbench/services/files2/common/fileService2.ts +++ b/src/vs/workbench/services/files2/common/fileService2.ts @@ -1,18 +1,18 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Disposable, IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import { IFileService, IResolveFileOptions, IResourceEncodings, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, IResolveContentOptions, IContent, IStreamContent, ITextSnapshot, IUpdateContentOptions, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat } from 'vs/platform/files/common/files'; +import { IFileService, IResolveFileOptions, IResourceEncodings, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, IResolveContentOptions, IContent, IStreamContent, ITextSnapshot, IUpdateContentOptions, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { isAbsolutePath, dirname, basename, joinPath, isEqual } from 'vs/base/common/resources'; +import { isAbsolutePath, dirname, basename, joinPath, isEqual, isEqualOrParent } from 'vs/base/common/resources'; import { localize } from 'vs/nls'; import { TernarySearchTree } from 'vs/base/common/map'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; +import { isNonEmptyArray, coalesce } from 'vs/base/common/arrays'; import { getBaseLabel } from 'vs/base/common/labels'; import { ILogService } from 'vs/platform/log/common/log'; @@ -108,10 +108,7 @@ export class FileService2 extends Disposable implements IFileService { // Assert path is absolute if (!isAbsolutePath(resource)) { - throw new FileOperationError( - localize('invalidPath', "The path of resource '{0}' must be absolute", resource.toString(true)), - FileOperationResult.FILE_INVALID_PATH - ); + throw new FileOperationError(localize('invalidPath', "The path of resource '{0}' must be absolute", resource.toString(true)), FileOperationResult.FILE_INVALID_PATH); } // Activate provider @@ -137,6 +134,8 @@ export class FileService2 extends Disposable implements IFileService { //#region File Metadata Resolving + async resolveFile(resource: URI, options: IResolveMetadataFileOptions): Promise; + async resolveFile(resource: URI, options?: IResolveFileOptions): Promise; async resolveFile(resource: URI, options?: IResolveFileOptions): Promise { try { return await this.doResolveFile(resource, options); @@ -155,20 +154,25 @@ export class FileService2 extends Disposable implements IFileService { } } + private async doResolveFile(resource: URI, options: IResolveMetadataFileOptions): Promise; + private async doResolveFile(resource: URI, options?: IResolveFileOptions): Promise; private async doResolveFile(resource: URI, options?: IResolveFileOptions): Promise { const provider = await this.withProvider(resource); // leverage a trie to check for recursive resolving - const to = options && options.resolveTo; + const resolveTo = options && options.resolveTo; const trie = TernarySearchTree.forPaths(); trie.set(resource.toString(), true); - if (isNonEmptyArray(to)) { - to.forEach(uri => trie.set(uri.toString(), true)); + if (isNonEmptyArray(resolveTo)) { + resolveTo.forEach(uri => trie.set(uri.toString(), true)); } + const resolveSingleChildDescendants = !!(options && options.resolveSingleChildDescendants); + const resolveMetadata = !!(options && options.resolveMetadata); + const stat = await provider.stat(resource); - return await this.toFileStat(provider, resource, stat, undefined, (stat, siblings) => { + return await this.toFileStat(provider, resource, stat, undefined, resolveMetadata, (stat, siblings) => { // check for recursive resolving if (Boolean(trie.findSuperstr(stat.resource.toString()) || trie.get(stat.resource.toString()))) { @@ -176,7 +180,7 @@ export class FileService2 extends Disposable implements IFileService { } // check for resolving single child folders - if (stat.isDirectory && options && options.resolveSingleChildDescendants) { + if (stat.isDirectory && resolveSingleChildDescendants) { return siblings === 1; } @@ -184,7 +188,7 @@ export class FileService2 extends Disposable implements IFileService { }); } - private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat, siblings: number | undefined, recurse: (stat: IFileStat, siblings?: number) => boolean): Promise { + private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat, siblings: number | undefined, resolveMetadata: boolean, recurse: (stat: IFileStat, siblings?: number) => boolean): Promise { // convert to file stat const fileStat: IFileStat = { @@ -195,20 +199,28 @@ export class FileService2 extends Disposable implements IFileService { isReadonly: !!(provider.capabilities & FileSystemProviderCapabilities.Readonly), mtime: stat.mtime, size: stat.size, - etag: stat.mtime.toString(29) + stat.size.toString(31), + etag: etag(stat.mtime, stat.size) }; // check to recurse for directories if (fileStat.isDirectory && recurse(fileStat, siblings)) { try { const entries = await provider.readdir(resource); + const resolvedEntries = await Promise.all(entries.map(async ([name, type]) => { + try { + const childResource = joinPath(resource, name); + const childStat = resolveMetadata ? await provider.stat(childResource) : { type }; - fileStat.children = await Promise.all(entries.map(async entry => { - const childResource = joinPath(resource, entry[0]); - const childStat = await provider.stat(childResource); + return this.toFileStat(provider, childResource, childStat, entries.length, resolveMetadata, recurse); + } catch (error) { + this.logService.trace(error); - return this.toFileStat(provider, childResource, childStat, entries.length, recurse); + return null; // can happen e.g. due to permission errors + } })); + + // make sure to get rid of null values that signal a failure to resolve a particular entry + fileStat.children = coalesce(resolvedEntries); } catch (error) { this.logService.trace(error); @@ -221,6 +233,8 @@ export class FileService2 extends Disposable implements IFileService { return Promise.resolve(fileStat); } + async resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise; + async resolveFiles(toResolve: { resource: URI, options: IResolveMetadataFileOptions }[]): Promise; async resolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): Promise { // soft-groupBy, keep order, don't rearrange/merge groups @@ -235,22 +249,23 @@ export class FileService2 extends Disposable implements IFileService { group.push(request); } - // resolve files - const result: IResolveFileResult[] = []; + // resolve files (in parallel) + const result: Promise[] = []; for (const group of groups) { for (const groupEntry of group) { - try { - const stat = await this.doResolveFile(groupEntry.resource, groupEntry.options); - result.push({ stat, success: true }); - } catch (error) { - this.logService.trace(error); + result.push((async () => { + try { + return { stat: await this.doResolveFile(groupEntry.resource, groupEntry.options), success: true }; + } catch (error) { + this.logService.trace(error); - result.push({ stat: undefined, success: false }); - } + return { stat: undefined, success: false }; + } + })()); } } - return result; + return Promise.all(result); } async existsFile(resource: URI): Promise { @@ -269,7 +284,7 @@ export class FileService2 extends Disposable implements IFileService { get encoding(): IResourceEncodings { return this._impl.encoding; } - createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise { + createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise { return this._impl.createFile(resource, content, options); } @@ -281,7 +296,7 @@ export class FileService2 extends Disposable implements IFileService { return this._impl.resolveStreamContent(resource, options); } - updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise { + updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise { return this._impl.updateContent(resource, value, options); } @@ -289,22 +304,102 @@ export class FileService2 extends Disposable implements IFileService { //#region Move/Copy/Delete/Create Folder - moveFile(source: URI, target: URI, overwrite?: boolean): Promise { - return this._impl.moveFile(source, target, overwrite); + moveFile(source: URI, target: URI, overwrite?: boolean): Promise { + if (source.scheme === target.scheme) { + return this.doMoveCopyWithSameProvider(source, target, false /* just move */, overwrite); + } + + return this.doMoveWithDifferentProvider(source, target); } - copyFile(source: URI, target: URI, overwrite?: boolean): Promise { - return this._impl.copyFile(source, target, overwrite); + private async doMoveWithDifferentProvider(source: URI, target: URI, overwrite?: boolean): Promise { + + // copy file source => target + await this.copyFile(source, target, overwrite); + + // delete source + await this.del(source, { recursive: true }); + + // resolve and send events + const fileStat = await this.resolveFile(target, { resolveMetadata: true }); + this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.MOVE, fileStat)); + + return fileStat; } - async createFolder(resource: URI): Promise { + async copyFile(source: URI, target: URI, overwrite?: boolean): Promise { + if (source.scheme === target.scheme) { + return this.doCopyWithSameProvider(source, target, overwrite); + } + + return this.doCopyWithDifferentProvider(source, target); + } + + private async doCopyWithSameProvider(source: URI, target: URI, overwrite: boolean = false): Promise { + const provider = this.throwIfFileSystemIsReadonly(await this.withProvider(source)); + + // check if provider supports fast file/folder copy + if (provider.capabilities & FileSystemProviderCapabilities.FileFolderCopy && typeof provider.copy === 'function') { + return this.doMoveCopyWithSameProvider(source, target, true /* keep copy */, overwrite); + } + + return this._impl.copyFile(source, target, overwrite); // TODO@ben implement properly + } + + private async doCopyWithDifferentProvider(source: URI, target: URI, overwrite?: boolean): Promise { + return this._impl.copyFile(source, target, overwrite); // TODO@ben implement properly + } + + private async doMoveCopyWithSameProvider(source: URI, target: URI, keepCopy: boolean, overwrite?: boolean): Promise { + const provider = this.throwIfFileSystemIsReadonly(await this.withProvider(source)); + + // validation + const isPathCaseSensitive = !!(provider.capabilities & FileSystemProviderCapabilities.PathCaseSensitive); + const isCaseChange = isPathCaseSensitive ? false : isEqual(source, target, true /* ignore case */); + if (!isCaseChange && isEqualOrParent(target, source, !isPathCaseSensitive)) { + return Promise.reject(new Error(localize('unableToMoveCopyError1', "Unable to move/copy when source path is equal or parent of target path"))); + } + + const exists = await this.existsFile(target); + if (exists && !isCaseChange) { + if (!overwrite) { + throw new FileOperationError(localize('unableToMoveCopyError2', "Unable to move/copy. File already exists at destination."), FileOperationResult.FILE_MOVE_CONFLICT); + } + + // Special case: if the target is a parent of the source, we cannot delete + // it as it would delete the source as well. In this case we have to throw + if (isEqualOrParent(source, target, !isPathCaseSensitive)) { + return Promise.reject(new Error(localize('unableToMoveCopyError3', "Unable to move/copy. File would replace folder it is contained in."))); + } + + await this.del(target, { recursive: true }); + } + + // create parent folders + await this.mkdirp(provider, dirname(target)); + + // rename/copy source => target + if (keepCopy) { + await provider.copy!(source, target, { overwrite: !!overwrite }); + } else { + await provider.rename(source, target, { overwrite: !!overwrite }); + } + + // resolve and send events + const fileStat = await this.resolveFile(target, { resolveMetadata: true }); + this._onAfterOperation.fire(new FileOperationEvent(source, keepCopy ? FileOperation.COPY : FileOperation.MOVE, fileStat)); + + return fileStat; + } + + async createFolder(resource: URI): Promise { const provider = this.throwIfFileSystemIsReadonly(await this.withProvider(resource)); // mkdir recursively await this.mkdirp(provider, resource); // events - const fileStat = await this.resolveFile(resource); + const fileStat = await this.resolveFile(resource, { resolveMetadata: true }); this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat)); return fileStat; @@ -318,7 +413,7 @@ export class FileService2 extends Disposable implements IFileService { try { const stat = await provider.stat(directory); if ((stat.type & FileType.Directory) === 0) { - throw new Error(`${directory.toString()} exists, but is not a directory`); + throw new Error(localize('mkdirExistsError', "{0} exists, but is not a directory", directory.toString())); } break; // we have hit a directory that exists -> good diff --git a/src/vs/workbench/services/files2/node/diskFileSystemProvider.ts b/src/vs/workbench/services/files2/node/diskFileSystemProvider.ts index 383e557d84..69894c5776 100644 --- a/src/vs/workbench/services/files2/node/diskFileSystemProvider.ts +++ b/src/vs/workbench/services/files2/node/diskFileSystemProvider.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { mkdir } from 'fs'; @@ -11,7 +11,7 @@ import { IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatc import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { isLinux } from 'vs/base/common/platform'; -import { statLink, readdir, unlink, del } from 'vs/base/node/pfs'; +import { statLink, readdir, unlink, del, move, copy } from 'vs/base/node/pfs'; import { normalize } from 'vs/base/common/path'; import { joinPath } from 'vs/base/common/resources'; @@ -132,12 +132,26 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro } } - rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { - throw new Error('Method not implemented.'); + async rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + try { + const fromFilePath = this.toFilePath(from); + const toFilePath = this.toFilePath(to); + + await move(fromFilePath, toFilePath); + } catch (error) { + throw this.toFileSystemProviderError(error); + } } - copy?(from: URI, to: URI, opts: FileOverwriteOptions): Promise { - throw new Error('Method not implemented.'); + async copy(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + try { + const fromFilePath = this.toFilePath(from); + const toFilePath = this.toFilePath(to); + + return copy(fromFilePath, toFilePath); + } catch (error) { + throw this.toFileSystemProviderError(error); + } } //#endregion @@ -160,6 +174,10 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro } private toFileSystemProviderError(error: NodeJS.ErrnoException): FileSystemProviderError { + if (error instanceof FileSystemProviderError) { + return error; // avoid double conversion + } + let code: FileSystemProviderErrorCode | undefined = undefined; switch (error.code) { case 'ENOENT': diff --git a/src/vs/workbench/services/files2/node/diskFileSystemSupport.ts b/src/vs/workbench/services/files2/node/diskFileSystemSupport.ts index 502cfc2db4..a01696ed1f 100644 --- a/src/vs/workbench/services/files2/node/diskFileSystemSupport.ts +++ b/src/vs/workbench/services/files2/node/diskFileSystemSupport.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; diff --git a/src/vs/workbench/services/files2/test/browser/fileService2.test.ts b/src/vs/workbench/services/files2/test/browser/fileService2.test.ts index f97d7d8a31..618a328b38 100644 --- a/src/vs/workbench/services/files2/test/browser/fileService2.test.ts +++ b/src/vs/workbench/services/files2/test/browser/fileService2.test.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; diff --git a/src/vs/workbench/services/files2/test/node/diskFileService.test.ts b/src/vs/workbench/services/files2/test/node/diskFileService.test.ts index 9a4e18f19f..dd20636297 100644 --- a/src/vs/workbench/services/files2/test/node/diskFileService.test.ts +++ b/src/vs/workbench/services/files2/test/node/diskFileService.test.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; @@ -10,12 +10,12 @@ import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/workbench/services/files2/node/diskFileSystemProvider'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { generateUuid } from 'vs/base/common/uuid'; -import { join, basename } from 'vs/base/common/path'; +import { join, basename, dirname, posix } from 'vs/base/common/path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { copy, del } from 'vs/base/node/pfs'; import { URI } from 'vs/base/common/uri'; -import { existsSync } from 'fs'; -import { FileOperation, FileOperationEvent, IFileStat } from 'vs/platform/files/common/files'; +import { existsSync, statSync, readdirSync } from 'fs'; +import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult } from 'vs/platform/files/common/files'; import { FileService } from 'vs/workbench/services/files/node/fileService'; import { TestContextService, TestEnvironmentService, TestTextResourceConfigurationService, TestLifecycleService, TestStorageService } from 'vs/workbench/test/workbenchTestServices'; import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; @@ -140,13 +140,48 @@ suite('Disk File Service', () => { assert.ok(result!.isDirectory); assert.equal(result.children!.length, testsElements.length); - assert.ok(result.children!.every((entry) => { - return testsElements.some((name) => { + assert.ok(result.children!.every(entry => { + return testsElements.some(name => { return basename(entry.resource.fsPath) === name; }); })); - result.children!.forEach((value) => { + result.children!.forEach(value => { + assert.ok(basename(value.resource.fsPath)); + if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) { + assert.ok(value.isDirectory); + } else if (basename(value.resource.fsPath) === 'index.html') { + assert.ok(!value.isDirectory); + assert.ok(!value.children); + } else if (basename(value.resource.fsPath) === 'site.css') { + assert.ok(!value.isDirectory); + assert.ok(!value.children); + } else { + assert.ok(!'Unexpected value ' + basename(value.resource.fsPath)); + } + }); + }); + + test('resolveFile - directory - with metadata', async () => { + const testsElements = ['examples', 'other', 'index.html', 'site.css']; + + const result = await service.resolveFile(URI.file(getPathFromAmdModule(require, './fixtures/resolver')), { resolveMetadata: true }); + + assert.ok(result); + assert.ok(result.children); + assert.ok(result.children!.length > 0); + assert.ok(result!.isDirectory); + assert.equal(result.children!.length, testsElements.length); + + assert.ok(result.children!.every(entry => { + return testsElements.some(name => { + return basename(entry.resource.fsPath) === name; + }); + })); + + assert.ok(result.children!.every(entry => entry.etag.length > 0)); + + result.children!.forEach(value => { assert.ok(basename(value.resource.fsPath)); if (['examples', 'other'].indexOf(basename(value.resource.fsPath)) >= 0) { assert.ok(value.isDirectory); @@ -300,4 +335,312 @@ suite('Disk File Service', () => { return Promise.resolve(true); } }); + + test('renameFile', async () => { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const resource = URI.file(join(testDir, 'index.html')); + const source = await service.resolveFile(resource); + + const renamed = await service.moveFile(source.resource, URI.file(join(dirname(source.resource.fsPath), 'other.html'))); + + assert.equal(existsSync(renamed.resource.fsPath), true); + assert.equal(existsSync(source.resource.fsPath), false); + assert.ok(event!); + assert.equal(event!.resource.fsPath, resource.fsPath); + assert.equal(event!.operation, FileOperation.MOVE); + assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath); + + toDispose.dispose(); + }); + + test('renameFile - multi folder', async () => { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const multiFolderPaths = ['a', 'couple', 'of', 'folders']; + const renameToPath = join(...multiFolderPaths, 'other.html'); + + const resource = URI.file(join(testDir, 'index.html')); + const source = await service.resolveFile(resource); + + const renamed = await service.moveFile(source.resource, URI.file(join(dirname(source.resource.fsPath), renameToPath))); + + assert.equal(existsSync(renamed.resource.fsPath), true); + assert.equal(existsSync(source.resource.fsPath), false); + assert.ok(event!); + assert.equal(event!.resource.fsPath, resource.fsPath); + assert.equal(event!.operation, FileOperation.MOVE); + assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath); + + toDispose.dispose(); + }); + + test('renameFolder', async () => { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const resource = URI.file(join(testDir, 'deep')); + const source = await service.resolveFile(resource); + + const renamed = await service.moveFile(source.resource, URI.file(join(dirname(source.resource.fsPath), 'deeper'))); + + assert.equal(existsSync(renamed.resource.fsPath), true); + assert.equal(existsSync(source.resource.fsPath), false); + assert.ok(event!); + assert.equal(event!.resource.fsPath, resource.fsPath); + assert.equal(event!.operation, FileOperation.MOVE); + assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath); + + toDispose.dispose(); + }); + + test('renameFolder - multi folder', async () => { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const multiFolderPaths = ['a', 'couple', 'of', 'folders']; + const renameToPath = join(...multiFolderPaths); + + const resource = URI.file(join(testDir, 'deep')); + const source = await service.resolveFile(resource); + + const renamed = await service.moveFile(source.resource, URI.file(join(dirname(source.resource.fsPath), renameToPath))); + + assert.equal(existsSync(renamed.resource.fsPath), true); + assert.equal(existsSync(source.resource.fsPath), false); + assert.ok(event!); + assert.equal(event!.resource.fsPath, resource.fsPath); + assert.equal(event!.operation, FileOperation.MOVE); + assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath); + + toDispose.dispose(); + }); + test('renameFile - MIX CASE', function () { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const resource = URI.file(join(testDir, 'index.html')); + return service.resolveFile(resource).then(source => { + return service.moveFile(source.resource, URI.file(join(dirname(source.resource.fsPath), 'INDEX.html'))).then(renamed => { + assert.equal(existsSync(renamed.resource.fsPath), true); + assert.equal(basename(renamed.resource.fsPath), 'INDEX.html'); + + assert.ok(event); + assert.equal(event.resource.fsPath, resource.fsPath); + assert.equal(event.operation, FileOperation.MOVE); + assert.equal(event.target!.resource.fsPath, renamed.resource.fsPath); + toDispose.dispose(); + }); + }); + }); + + test('deleteFolder (non recursive)', async () => { + const resource = URI.file(join(testDir, 'deep')); + const source = await service.resolveFile(resource); + try { + await service.del(source.resource); + return Promise.reject(new Error('Unexpected')); + } + catch (error) { + return Promise.resolve(true); + } + }); + + test('moveFile', async () => { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const resource = URI.file(join(testDir, 'index.html')); + const source = await service.resolveFile(resource); + + const renamed = await service.moveFile(source.resource, URI.file(join(testDir, 'other.html'))); + + assert.equal(existsSync(renamed.resource.fsPath), true); + assert.equal(existsSync(source.resource.fsPath), false); + assert.ok(event!); + assert.equal(event!.resource.fsPath, resource.fsPath); + assert.equal(event!.operation, FileOperation.MOVE); + assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath); + + toDispose.dispose(); + }); + + test('move - source parent of target', async () => { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + await service.resolveFile(URI.file(join(testDir, 'index.html'))); + try { + await service.moveFile(URI.file(testDir), URI.file(join(testDir, 'binary.txt'))); + } catch (e) { + assert.ok(e); + assert.ok(!event!); + toDispose.dispose(); + } + }); + + test('move - FILE_MOVE_CONFLICT', async () => { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const source = await service.resolveFile(URI.file(join(testDir, 'index.html'))); + try { + await service.moveFile(source.resource, URI.file(join(testDir, 'binary.txt'))); + } catch (e) { + assert.equal(e.fileOperationResult, FileOperationResult.FILE_MOVE_CONFLICT); + assert.ok(!event!); + toDispose.dispose(); + } + }); + + test('moveFile - MIX CASE', async () => { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const resource = URI.file(join(testDir, 'index.html')); + const source = await service.resolveFile(resource); + + const renamed = await service.moveFile(source.resource, URI.file(join(testDir, 'INDEX.html'))); + + assert.equal(existsSync(renamed.resource.fsPath), true); + assert.equal(basename(renamed.resource.fsPath), 'INDEX.html'); + assert.ok(event!); + assert.equal(event!.resource.fsPath, resource.fsPath); + assert.equal(event!.operation, FileOperation.MOVE); + assert.equal(event!.target!.resource.fsPath, renamed.resource.fsPath); + + toDispose.dispose(); + }); + + test('moveFile - overwrite folder with file', async () => { + let createEvent: FileOperationEvent; + let moveEvent: FileOperationEvent; + let deleteEvent: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + if (e.operation === FileOperation.CREATE) { + createEvent = e; + } else if (e.operation === FileOperation.DELETE) { + deleteEvent = e; + } else if (e.operation === FileOperation.MOVE) { + moveEvent = e; + } + }); + + const parent = await service.resolveFile(URI.file(testDir)); + const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js')); + const f = await service.createFolder(folderResource); + const resource = URI.file(join(testDir, 'deep', 'conway.js')); + + const moved = await service.moveFile(resource, f.resource, true); + + assert.equal(existsSync(moved.resource.fsPath), true); + assert.ok(statSync(moved.resource.fsPath).isFile); + assert.ok(createEvent!); + assert.ok(deleteEvent!); + assert.ok(moveEvent!); + assert.equal(moveEvent!.resource.fsPath, resource.fsPath); + assert.equal(moveEvent!.target!.resource.fsPath, moved.resource.fsPath); + assert.equal(deleteEvent!.resource.fsPath, folderResource.fsPath); + + toDispose.dispose(); + }); + + test('copyFile', async () => { + let event: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + event = e; + }); + + const source = await service.resolveFile(URI.file(join(testDir, 'index.html'))); + const resource = URI.file(join(testDir, 'other.html')); + + const copied = await service.copyFile(source.resource, resource); + + assert.equal(existsSync(copied.resource.fsPath), true); + assert.equal(existsSync(source.resource.fsPath), true); + assert.ok(event!); + assert.equal(event!.resource.fsPath, source.resource.fsPath); + assert.equal(event!.operation, FileOperation.COPY); + assert.equal(event!.target!.resource.fsPath, copied.resource.fsPath); + toDispose.dispose(); + }); + + test('copyFile - overwrite folder with file', async () => { + let createEvent: FileOperationEvent; + let copyEvent: FileOperationEvent; + let deleteEvent: FileOperationEvent; + const toDispose = service.onAfterOperation(e => { + if (e.operation === FileOperation.CREATE) { + createEvent = e; + } else if (e.operation === FileOperation.DELETE) { + deleteEvent = e; + } else if (e.operation === FileOperation.COPY) { + copyEvent = e; + } + }); + + const parent = await service.resolveFile(URI.file(testDir)); + const folderResource = URI.file(join(parent.resource.fsPath, 'conway.js')); + const f = await service.createFolder(folderResource); + const resource = URI.file(join(testDir, 'deep', 'conway.js')); + + const copied = await service.copyFile(resource, f.resource, true); + + assert.equal(existsSync(copied.resource.fsPath), true); + assert.ok(statSync(copied.resource.fsPath).isFile); + assert.ok(createEvent!); + assert.ok(deleteEvent!); + assert.ok(copyEvent!); + assert.equal(copyEvent!.resource.fsPath, resource.fsPath); + assert.equal(copyEvent!.target!.resource.fsPath, copied.resource.fsPath); + assert.equal(deleteEvent!.resource.fsPath, folderResource.fsPath); + + toDispose.dispose(); + }); + + test('copyFile - MIX CASE', async () => { + const source = await service.resolveFile(URI.file(join(testDir, 'index.html'))); + const renamed = await service.moveFile(source.resource, URI.file(join(dirname(source.resource.fsPath), 'CONWAY.js'))); + assert.equal(existsSync(renamed.resource.fsPath), true); + assert.ok(readdirSync(testDir).some(f => f === 'CONWAY.js')); + const source_1 = await service.resolveFile(URI.file(join(testDir, 'deep', 'conway.js'))); + const targetParent = URI.file(testDir); + const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source_1.resource.path)) }); + + const res = await service.copyFile(source_1.resource, target, true); + assert.equal(existsSync(res.resource.fsPath), true); + assert.ok(readdirSync(testDir).some(f => f === 'conway.js')); + }); + + test('copyFile - same file should throw', async () => { + const source = await service.resolveFile(URI.file(join(testDir, 'index.html'))); + const targetParent = URI.file(dirname(source.resource.fsPath)); + const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) }); + + try { + await service.copyFile(source.resource, target, true); + } catch (error) { + assert.ok(error); + } + }); }); diff --git a/src/vs/workbench/services/files2/test/node/fixtures/resolver/site.css b/src/vs/workbench/services/files2/test/node/fixtures/resolver/site.css index f7b51e752b..b7e5283202 100644 --- a/src/vs/workbench/services/files2/test/node/fixtures/resolver/site.css +++ b/src/vs/workbench/services/files2/test/node/fixtures/resolver/site.css @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ /*---------------------------------------------------------- diff --git a/src/vs/workbench/services/hash/common/hashService.ts b/src/vs/workbench/services/hash/common/hashService.ts index 4a9eafba6d..90df38dcf0 100644 --- a/src/vs/workbench/services/hash/common/hashService.ts +++ b/src/vs/workbench/services/hash/common/hashService.ts @@ -24,7 +24,7 @@ export class HashService implements IHashService { createSHA1(content: string): Thenable { return crypto.subtle.digest('SHA-1', new TextEncoder().encode(content)).then(buffer => { // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#Converting_a_digest_to_a_hex_string - return Array.prototype.map.call(new Uint8Array(buffer), value => `00${value.toString(16)}`.slice(-2)).join(''); + return Array.prototype.map.call(new Uint8Array(buffer), (value: number) => `00${value.toString(16)}`.slice(-2)).join(''); }); } } diff --git a/src/vs/workbench/services/heap/common/heap.ts b/src/vs/workbench/services/heap/common/heap.ts index 4aa4cc0ec7..a5823376e6 100644 --- a/src/vs/workbench/services/heap/common/heap.ts +++ b/src/vs/workbench/services/heap/common/heap.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ diff --git a/src/vs/workbench/services/keybinding/common/keybindingIO.ts b/src/vs/workbench/services/keybinding/common/keybindingIO.ts index 56de649039..b7e206b7bf 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingIO.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingIO.ts @@ -14,7 +14,7 @@ export interface IUserKeybindingItem { parts: (SimpleKeybinding | ScanCodeBinding)[]; command: string | null; commandArgs?: any; - when: ContextKeyExpr | null; + when: ContextKeyExpr | undefined; } export class KeybindingIO { @@ -41,7 +41,7 @@ export class KeybindingIO { public static readUserKeybindingItem(input: IUserFriendlyKeybinding): IUserKeybindingItem { const parts = (typeof input.key === 'string' ? KeybindingParser.parseUserBinding(input.key) : []); - const when = (typeof input.when === 'string' ? ContextKeyExpr.deserialize(input.when) : null); + const when = (typeof input.when === 'string' ? ContextKeyExpr.deserialize(input.when) : undefined); const command = (typeof input.command === 'string' ? input.command : null); const commandArgs = (typeof input.args !== 'undefined' ? input.args : undefined); return { diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index e7d646fc47..6b54213771 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -392,7 +392,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { private _resolveKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { let result: ResolvedKeybindingItem[] = [], resultLen = 0; for (const item of items) { - const when = (item.when ? item.when.normalize() : null); + const when = (item.when ? item.when.normalize() : undefined); const keybinding = item.keybinding; if (!keybinding) { // This might be a removal keybinding item in user settings => accept it @@ -411,7 +411,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { private _resolveUserKeybindingItems(items: IUserKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { let result: ResolvedKeybindingItem[] = [], resultLen = 0; for (const item of items) { - const when = (item.when ? item.when.normalize() : null); + const when = (item.when ? item.when.normalize() : undefined); const parts = item.parts; if (parts.length === 0) { // This might be a removal keybinding item in user settings => accept it diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts index a97f2ab9c0..fe748292b3 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts @@ -283,7 +283,7 @@ suite('KeybindingsEditing', () => { } } let keybinding = parts.length > 0 ? new USLayoutResolvedKeybinding(new ChordKeybinding(parts), OS) : null; - return new ResolvedKeybindingItem(keybinding, command || 'some command', null, when ? ContextKeyExpr.deserialize(when) : null, isDefault === undefined ? true : isDefault); + return new ResolvedKeybindingItem(keybinding, command || 'some command', null, when ? ContextKeyExpr.deserialize(when) : undefined, isDefault === undefined ? true : isDefault); } }); diff --git a/src/vs/workbench/services/output/common/outputChannelModel.ts b/src/vs/workbench/services/output/common/outputChannelModel.ts index 2672dc3cdd..8e8720cf65 100644 --- a/src/vs/workbench/services/output/common/outputChannelModel.ts +++ b/src/vs/workbench/services/output/common/outputChannelModel.ts @@ -160,7 +160,7 @@ class OutputFileListener extends Disposable { } private doWatch(): Promise { - return this.fileService.resolveFile(this.file) + return this.fileService.resolveFile(this.file, { resolveMetadata: true }) .then(stat => { if (stat.etag !== this.etag) { this.etag = stat.etag; diff --git a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts index 50a82f1136..cab5d30451 100644 --- a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts +++ b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts @@ -174,7 +174,7 @@ export class KeybindingsEditorModel extends EditorModel { const commandsWithDefaultKeybindings = this.keybindingsService.getDefaultKeybindings().map(keybinding => keybinding.command); for (const command of KeybindingResolver.getAllUnboundCommands(boundCommands)) { - const keybindingItem = new ResolvedKeybindingItem(null, command, null, null, commandsWithDefaultKeybindings.indexOf(command) === -1); + const keybindingItem = new ResolvedKeybindingItem(null, command, null, undefined, commandsWithDefaultKeybindings.indexOf(command) === -1); this._keybindingItemsSortedByPrecedence.push(KeybindingsEditorModel.toKeybindingEntry(command, keybindingItem, workbenchActionsRegistry, editorActionsLabels)); } this._keybindingItems = this._keybindingItemsSortedByPrecedence.slice(0).sort((a, b) => KeybindingsEditorModel.compareKeybindingData(a, b)); diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index f6309dfa41..d5bc290f27 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -959,7 +959,7 @@ class SettingsContentBuilder { } private pushSettingDescription(setting: ISetting, indent: string): void { - const fixSettingLink = line => line.replace(/`#(.*)#`/g, (match, settingName) => `\`${settingName}\``); + const fixSettingLink = (line: string) => line.replace(/`#(.*)#`/g, (match, settingName) => `\`${settingName}\``); setting.descriptionRanges = []; const descriptionPreValue = indent + '// '; @@ -1050,33 +1050,33 @@ export function createValidator(prop: IConfigurationPropertySchema): (value: any const numericValidations: Validator[] = isNumeric ? [ { enabled: exclusiveMax !== undefined && (prop.maximum === undefined || exclusiveMax <= prop.maximum), - isValid: (value => value < exclusiveMax!), + isValid: ((value: number) => value < exclusiveMax!), message: nls.localize('validations.exclusiveMax', "Value must be strictly less than {0}.", exclusiveMax) }, { enabled: exclusiveMin !== undefined && (prop.minimum === undefined || exclusiveMin >= prop.minimum), - isValid: (value => value > exclusiveMin!), + isValid: ((value: number) => value > exclusiveMin!), message: nls.localize('validations.exclusiveMin', "Value must be strictly greater than {0}.", exclusiveMin) }, { enabled: prop.maximum !== undefined && (exclusiveMax === undefined || exclusiveMax > prop.maximum), - isValid: (value => value <= prop.maximum!), + isValid: ((value: number) => value <= prop.maximum!), message: nls.localize('validations.max', "Value must be less than or equal to {0}.", prop.maximum) }, { enabled: prop.minimum !== undefined && (exclusiveMin === undefined || exclusiveMin < prop.minimum), - isValid: (value => value >= prop.minimum!), + isValid: ((value: number) => value >= prop.minimum!), message: nls.localize('validations.min', "Value must be greater than or equal to {0}.", prop.minimum) }, { enabled: prop.multipleOf !== undefined, - isValid: (value => value % prop.multipleOf! === 0), + isValid: ((value: number) => value % prop.multipleOf! === 0), message: nls.localize('validations.multipleOf', "Value must be a multiple of {0}.", prop.multipleOf) }, { enabled: isIntegral, - isValid: (value => value % 1 === 0), + isValid: ((value: number) => value % 1 === 0), message: nls.localize('validations.expectedInteger', "Value must be an integer.") }, ].filter(validation => validation.enabled) : []; @@ -1084,17 +1084,17 @@ export function createValidator(prop: IConfigurationPropertySchema): (value: any const stringValidations: Validator[] = [ { enabled: prop.maxLength !== undefined, - isValid: (value => value.length <= prop.maxLength!), + isValid: ((value: { length: number; }) => value.length <= prop.maxLength!), message: nls.localize('validations.maxLength', "Value must be {0} or fewer characters long.", prop.maxLength) }, { enabled: prop.minLength !== undefined, - isValid: (value => value.length >= prop.minLength!), + isValid: ((value: { length: number; }) => value.length >= prop.minLength!), message: nls.localize('validations.minLength', "Value must be {0} or more characters long.", prop.minLength) }, { enabled: patternRegex !== undefined, - isValid: (value => patternRegex!.test(value)), + isValid: ((value: string) => patternRegex!.test(value)), message: prop.patternErrorMessage || nls.localize('validations.regex', "Value must match regex `{0}`.", prop.pattern) }, ].filter(validation => validation.enabled); diff --git a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts index 8ca1b3bb52..9b6e031ec7 100644 --- a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts +++ b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts @@ -618,7 +618,7 @@ suite('KeybindingsEditorModel test', () => { } } let keybinding = parts.length > 0 ? new USLayoutResolvedKeybinding(new ChordKeybinding(parts), OS) : null; - return new ResolvedKeybindingItem(keybinding, command || 'some command', null, when ? ContextKeyExpr.deserialize(when) : null, isDefault === undefined ? true : isDefault); + return new ResolvedKeybindingItem(keybinding, command || 'some command', null, when ? ContextKeyExpr.deserialize(when) : undefined, isDefault === undefined ? true : isDefault); } function asResolvedKeybindingItems(keybindingEntries: IKeybindingItemEntry[], keepUnassigned: boolean = false): ResolvedKeybindingItem[] { diff --git a/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts b/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts index 9e83c4b145..ef335092af 100644 --- a/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts +++ b/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts @@ -16,15 +16,15 @@ suite('Preferences Model test', () => { this.validator = createValidator(settings)!; } - public accepts(input) { + public accepts(input: string) { assert.equal(this.validator(input), '', `Expected ${JSON.stringify(this.settings)} to accept \`${input}\`. Got ${this.validator(input)}.`); } - public rejects(input) { + public rejects(input: string) { assert.notEqual(this.validator(input), '', `Expected ${JSON.stringify(this.settings)} to reject \`${input}\`.`); return { withMessage: - (message) => { + (message: string) => { const actual = this.validator(input); assert.ok(actual); assert(actual!.indexOf(message) > -1, diff --git a/src/vs/workbench/services/remote/common/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts index 16562a3bd4..2cd6957ffb 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IRemoteAgentEnvironment, RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { RemoteAgentConnectionContext, IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; export const RemoteExtensionLogFileName = 'remoteagent'; @@ -15,13 +15,12 @@ export interface IRemoteAgentService { _serviceBrand: any; getConnection(): IRemoteAgentConnection | null; + getEnvironment(): Promise; } export interface IRemoteAgentConnection { readonly remoteAuthority: string; - getEnvironment(): Promise; - getChannel(channelName: string): T; registerChannel>(channelName: string, channel: T): void; } diff --git a/src/vs/workbench/services/remote/common/remoteEnvironmentService.ts b/src/vs/workbench/services/remote/common/remoteEnvironmentService.ts deleted file mode 100644 index ab6d619b4f..0000000000 --- a/src/vs/workbench/services/remote/common/remoteEnvironmentService.ts +++ /dev/null @@ -1,34 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export interface IRemoteEnvironmentService { - _serviceBrand: any; - remoteEnvironment: Promise; -} - -export const IRemoteEnvironmentService = createDecorator('remoteEnvironmentService'); - -export class RemoteEnvironmentService implements IRemoteEnvironmentService { - _serviceBrand: any; - - constructor( - @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, - ) { } - - get remoteEnvironment(): Promise { - const connection = this.remoteAgentService.getConnection(); - if (connection) { - return connection.getEnvironment(); - } - return Promise.resolve(null); - } -} - -registerSingleton(IRemoteEnvironmentService, RemoteEnvironmentService, true); diff --git a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts index 4b6d50e306..394af585cd 100644 --- a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts @@ -3,86 +3,72 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { getDelayedChannel } from 'vs/base/parts/ipc/node/ipc'; import { Client } from 'vs/base/parts/ipc/node/ipc.net'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { connectRemoteAgentManagement } from 'vs/platform/remote/node/remoteAgentConnection'; -import { IWindowService } from 'vs/platform/windows/common/windows'; -import { RemoteExtensionEnvironmentChannelClient } from 'vs/workbench/services/remote/node/remoteAgentEnvironmentChannel'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { DialogChannel } from 'vs/platform/dialogs/node/dialogIpc'; -import { DownloadServiceChannel } from 'vs/platform/download/node/downloadIpc'; -import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc'; -import { ILogService } from 'vs/platform/log/common/log'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IRemoteAgentEnvironment, RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { RemoteAgentConnectionContext, IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { RemoteExtensionEnvironmentChannelClient } from 'vs/workbench/services/remote/node/remoteAgentEnvironmentChannel'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { localize } from 'vs/nls'; -export class RemoteAgentService implements IRemoteAgentService { +export class RemoteAgentService extends Disposable implements IRemoteAgentService { _serviceBrand: any; private readonly _connection: IRemoteAgentConnection | null = null; + private _environment: Promise | null; constructor( - @IWindowService windowService: IWindowService, - @INotificationService notificationService: INotificationService, - @IEnvironmentService environmentService: IEnvironmentService, - @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, - @ILifecycleService lifecycleService: ILifecycleService, - @ILogService logService: ILogService, - @IInstantiationService instantiationService: IInstantiationService + { remoteAuthority }: IWindowConfiguration, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService ) { - const { remoteAuthority } = windowService.getConfiguration(); + super(); if (remoteAuthority) { - const connection = this._connection = new RemoteAgentConnection(remoteAuthority, notificationService, environmentService, remoteAuthorityResolverService); - - lifecycleService.when(LifecyclePhase.Ready).then(() => { - connection.registerChannel('dialog', instantiationService.createInstance(DialogChannel)); - connection.registerChannel('download', new DownloadServiceChannel()); - connection.registerChannel('loglevel', new LogLevelSetterChannel(logService)); - }); + this._connection = this._register(new RemoteAgentConnection(remoteAuthority, _environmentService, remoteAuthorityResolverService)); } } getConnection(): IRemoteAgentConnection | null { return this._connection; } + + getEnvironment(bail?: boolean): Promise { + if (!this._environment) { + const connection = this.getConnection(); + if (connection) { + const client = new RemoteExtensionEnvironmentChannelClient(connection.getChannel('remoteextensionsenvironment')); + this._environment = client.getEnvironmentData(connection.remoteAuthority, this._environmentService.extensionDevelopmentLocationURI); + } else { + this._environment = Promise.resolve(null); + } + } + return bail ? this._environment : this._environment.then(undefined, () => null); + } } class RemoteAgentConnection extends Disposable implements IRemoteAgentConnection { readonly remoteAuthority: string; private _connection: Promise> | null; - private _environment: Promise | null; constructor( remoteAuthority: string, - private _notificationService: INotificationService, private _environmentService: IEnvironmentService, private _remoteAuthorityResolverService: IRemoteAuthorityResolverService ) { super(); this.remoteAuthority = remoteAuthority; this._connection = null; - this._environment = null; - } - - getEnvironment(): Promise { - if (!this._environment) { - const client = new RemoteExtensionEnvironmentChannelClient(this.getChannel('remoteextensionsenvironment')); - - // Let's cover the case where connecting to fetch the remote extension info fails - this._environment = client.getEnvironmentData(this.remoteAuthority, this._environmentService.extensionDevelopmentLocationURI) - .then(undefined, err => { this._notificationService.error(localize('connectionError', "Failed to connect to the remote extension host agent (Error: {0})", err ? err.message : '')); return null; }); - } - return this._environment; } getChannel(channelName: string): T { @@ -108,4 +94,18 @@ class RemoteAgentConnection extends Disposable implements IRemoteAgentConnection } } -registerSingleton(IRemoteAgentService, RemoteAgentService); \ No newline at end of file +class RemoteConnectionFailureNotificationContribution implements IWorkbenchContribution { + + constructor( + @IRemoteAgentService remoteAgentService: RemoteAgentService, + @INotificationService notificationService: INotificationService, + ) { + // Let's cover the case where connecting to fetch the remote extension info fails + remoteAgentService.getEnvironment(true) + .then(undefined, err => notificationService.error(localize('connectionError', "Failed to connect to the remote extension host agent (Error: {0})", err ? err.message : ''))); + } + +} + +const workbenchRegistry = Registry.as(Extensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(RemoteConnectionFailureNotificationContribution, LifecyclePhase.Ready); \ No newline at end of file diff --git a/src/vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts b/src/vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts index d159e505c9..22b45d4e03 100644 --- a/src/vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts +++ b/src/vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts @@ -19,6 +19,7 @@ export interface IRemoteAgentEnvironmentDTO { pid: number; appRoot: UriComponents; appSettingsHome: UriComponents; + appSettingsPath: UriComponents; logsPath: UriComponents; extensionsPath: UriComponents; extensionHostLogsPath: UriComponents; @@ -45,6 +46,7 @@ export class RemoteExtensionEnvironmentChannelClient { pid: data.pid, appRoot: URI.revive(data.appRoot), appSettingsHome: URI.revive(data.appSettingsHome), + appSettingsPath: URI.revive(data.appSettingsPath), logsPath: URI.revive(data.logsPath), extensionsPath: URI.revive(data.extensionsPath), extensionHostLogsPath: URI.revive(data.extensionHostLogsPath), diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index ba65310d20..341bc9c47d 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -181,13 +181,19 @@ export function resultIsMatch(result: ITextSearchResult): result is ITextSearchM return !!(result).preview; } -export interface IProgress { - total?: number; - worked?: number; +export interface IProgressMessage { message?: string; } -export type ISearchProgressItem = IFileMatch | IProgress; +export type ISearchProgressItem = IFileMatch | IProgressMessage; + +export function isFileMatch(p: ISearchProgressItem): p is IFileMatch { + return !!(p).resource; +} + +export function isProgressMessage(p: ISearchProgressItem): p is IProgressMessage { + return !isFileMatch(p); +} export interface ISearchCompleteStats { limitHit?: boolean; @@ -410,7 +416,7 @@ export interface IRawFileMatch { } export interface ISearchEngine { - search: (onResult: (matches: T) => void, onProgress: (progress: IProgress) => void, done: (error: Error | null, complete: ISearchEngineSuccess) => void) => void; + search: (onResult: (matches: T) => void, onProgress: (progress: IProgressMessage) => void, done: (error: Error | null, complete: ISearchEngineSuccess) => void) => void; cancel: () => void; } @@ -460,8 +466,8 @@ export interface ISerializedFileMatch { } // Type of the possible values for progress calls from the engine -export type ISerializedSearchProgressItem = ISerializedFileMatch | ISerializedFileMatch[] | IProgress; -export type IFileSearchProgressItem = IRawFileMatch | IRawFileMatch[] | IProgress; +export type ISerializedSearchProgressItem = ISerializedFileMatch | ISerializedFileMatch[] | IProgressMessage; +export type IFileSearchProgressItem = IRawFileMatch | IRawFileMatch[] | IProgressMessage; export class SerializableFileMatch implements ISerializedFileMatch { diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index ffa57cefd3..539934c056 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -21,7 +21,7 @@ import * as types from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import * as extfs from 'vs/base/node/extfs'; import * as flow from 'vs/base/node/flow'; -import { IFileQuery, IFolderQuery, IProgress, ISearchEngineStats, IRawFileMatch, ISearchEngine, ISearchEngineSuccess } from 'vs/workbench/services/search/common/search'; +import { IFileQuery, IFolderQuery, IProgressMessage, ISearchEngineStats, IRawFileMatch, ISearchEngine, ISearchEngineSuccess } from 'vs/workbench/services/search/common/search'; import { spawnRipgrepCmd } from './ripgrepFileSearch'; interface IDirectoryEntry { @@ -106,7 +106,7 @@ export class FileWalker { this.isCanceled = true; } - walk(folderQueries: IFolderQuery[], extraFiles: URI[], onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgress) => void, done: (error: Error | null, isLimitHit: boolean) => void): void { + walk(folderQueries: IFolderQuery[], extraFiles: URI[], onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgressMessage) => void, done: (error: Error | null, isLimitHit: boolean) => void): void { this.fileWalkSW = StopWatch.create(false); // Support that the file pattern is a full path to a file that exists @@ -154,7 +154,7 @@ export class FileWalker { } } - private cmdTraversal(folderQuery: IFolderQuery, onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgress) => void, cb: (err?: Error) => void): void { + private cmdTraversal(folderQuery: IFolderQuery, onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgressMessage) => void, cb: (err?: Error) => void): void { const rootFolder = folderQuery.folder.fsPath; const isMac = platform.isMacintosh; let cmd: childProcess.ChildProcess; @@ -285,7 +285,7 @@ export class FileWalker { }); } - private collectStdout(cmd: childProcess.ChildProcess, encoding: string, onMessage: (message: IProgress) => void, cb: (err: Error | null, stdout?: string, last?: boolean) => void): void { + private collectStdout(cmd: childProcess.ChildProcess, encoding: string, onMessage: (message: IProgressMessage) => void, cb: (err: Error | null, stdout?: string, last?: boolean) => void): void { let onData = (err: Error | null, stdout?: string, last?: boolean) => { if (err || last) { onData = () => { }; @@ -590,7 +590,7 @@ export class Engine implements ISearchEngine { this.walker = new FileWalker(config); } - search(onResult: (result: IRawFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISearchEngineSuccess) => void): void { + search(onResult: (result: IRawFileMatch) => void, onProgress: (progress: IProgressMessage) => void, done: (error: Error, complete: ISearchEngineSuccess) => void): void { this.walker.walk(this.folderQueries, this.extraFiles, onResult, onProgress, (err: Error, isLimitHit: boolean) => { done(err, { limitHit: isLimitHit, diff --git a/src/vs/workbench/services/search/node/fileSearchManager.ts b/src/vs/workbench/services/search/node/fileSearchManager.ts index b89155565e..15398775e6 100644 --- a/src/vs/workbench/services/search/node/fileSearchManager.ts +++ b/src/vs/workbench/services/search/node/fileSearchManager.ts @@ -344,7 +344,7 @@ export class FileSearchManager { engine.cancel(); }); - const _onResult = match => { + const _onResult = (match: IInternalFileMatch) => { if (match) { batch.push(match); if (batchSize > 0 && batch.length >= batchSize) { diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 2a4f5fb955..6b63bd17ae 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -17,14 +17,14 @@ import * as strings from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; import { MAX_FILE_SIZE } from 'vs/platform/files/node/fileConstants'; -import { ICachedSearchStats, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, IRawFileQuery, IRawQuery, IRawTextQuery, ITextQuery, IFileSearchProgressItem, IRawFileMatch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from 'vs/workbench/services/search/common/search'; +import { ICachedSearchStats, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, IRawFileQuery, IRawQuery, IRawTextQuery, ITextQuery, IFileSearchProgressItem, IRawFileMatch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from 'vs/workbench/services/search/common/search'; import { Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; gracefulFs.gracefulify(fs); -type IProgressCallback = (p: ISerializedSearchProgressItem) => void; -type IFileProgressCallback = (p: IFileSearchProgressItem) => void; +export type IProgressCallback = (p: ISerializedSearchProgressItem) => void; +export type IFileProgressCallback = (p: IFileSearchProgressItem) => void; export class SearchService implements IRawSearchService { @@ -97,7 +97,7 @@ export class SearchService implements IRawSearchService { resultCount++; progressCallback(this.rawMatchToSearchItem(progress)); } else { - progressCallback(progress); + progressCallback(progress); } }; @@ -383,13 +383,13 @@ export class SearchService implements IRawSearchService { cancel() { // Do nothing } - then(resolve, reject) { + then(resolve: any, reject: any) { return promise.then(resolve, reject); } - catch(reject?) { + catch(reject?: any) { return this.then(undefined, reject); } - finally(onFinally) { + finally(onFinally: any) { return promise.finally(onFinally); } }; diff --git a/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts b/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts index 63b14842b0..21915a0dd4 100644 --- a/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts +++ b/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts @@ -42,7 +42,7 @@ function searchRangeToRange(range: SearchRange): Range { } export class Position { - constructor(readonly line, readonly character) { } + constructor(readonly line: number, readonly character: number) { } isBefore(other: Position): boolean { return false; } isBeforeOrEqual(other: Position): boolean { return false; } diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts index 7e01a4934a..9c46e581dd 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts @@ -165,10 +165,11 @@ export class RipgrepParser extends EventEmitter { } - on(event: 'result', listener: (result: vscode.TextSearchResult) => void); - on(event: 'hitLimit', listener: () => void); - on(event: string, listener: (...args: any[]) => void) { + on(event: 'result', listener: (result: vscode.TextSearchResult) => void): this; + on(event: 'hitLimit', listener: () => void): this; + on(event: string, listener: (...args: any[]) => void): this { super.on(event, listener); + return this; } handleData(data: Buffer | string): void { diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index f9092c866a..b40a83f41a 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -26,7 +26,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, IRawSearchService, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType } from 'vs/workbench/services/search/common/search'; +import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, IRawSearchService, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType, isFileMatch, isProgressMessage } from 'vs/workbench/services/search/common/search'; import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { SearchChannelClient } from './searchIpc'; @@ -78,18 +78,18 @@ export class SearchService extends Disposable implements ISearchService { arrays.coalesce(localResults.values()).forEach(onProgress); } - const onProviderProgress = progress => { - if (progress.resource) { + const onProviderProgress = (progress: ISearchProgressItem) => { + if (isFileMatch(progress)) { // Match if (!localResults.has(progress.resource) && onProgress) { // don't override local results onProgress(progress); } } else if (onProgress) { // Progress - onProgress(progress); + onProgress(progress); } - if (progress.message) { + if (isProgressMessage(progress)) { this.logService.debug('SearchService#search', progress.message); } }; @@ -142,7 +142,7 @@ export class SearchService extends Disposable implements ISearchService { return { limitHit: completes[0] && completes[0].limitHit, stats: completes[0].stats, - results: arrays.flatten(completes.map(c => c.results)) + results: arrays.flatten(completes.map((c: ISearchComplete) => c.results)) }; }); @@ -497,7 +497,7 @@ export class DiskSearch implements ISearchResultProvider { let event: Event; event = this.raw.fileSearch(query); - const onProgress = (p: IProgress) => { + const onProgress = (p: IProgressMessage) => { if (p.message) { // Should only be for logs this.logService.debug('SearchService#search', p.message); @@ -561,7 +561,7 @@ export class DiskSearch implements ISearchResultProvider { // Progress else if (onProgress) { - onProgress(ev); + onProgress(ev); } } }); diff --git a/src/vs/workbench/services/search/node/textSearchAdapter.ts b/src/vs/workbench/services/search/node/textSearchAdapter.ts index 22ce6f7ccd..be96dce3db 100644 --- a/src/vs/workbench/services/search/node/textSearchAdapter.ts +++ b/src/vs/workbench/services/search/node/textSearchAdapter.ts @@ -5,7 +5,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import * as extfs from 'vs/base/node/extfs'; -import { IFileMatch, IProgress, ITextQuery, ITextSearchStats, ITextSearchMatch, ISerializedFileMatch, ISerializedSearchSuccess } from 'vs/workbench/services/search/common/search'; +import { IFileMatch, IProgressMessage, ITextQuery, ITextSearchStats, ITextSearchMatch, ISerializedFileMatch, ISerializedSearchSuccess } from 'vs/workbench/services/search/common/search'; import { RipgrepTextSearchEngine } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine'; import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager'; @@ -14,7 +14,7 @@ export class TextSearchEngineAdapter { constructor(private query: ITextQuery) { } - search(token: CancellationToken, onResult: (matches: ISerializedFileMatch[]) => void, onMessage: (message: IProgress) => void): Promise { + search(token: CancellationToken, onResult: (matches: ISerializedFileMatch[]) => void, onMessage: (message: IProgressMessage) => void): Promise { if ((!this.query.folderQueries || !this.query.folderQueries.length) && (!this.query.extraFileResources || !this.query.extraFileResources.length)) { return Promise.resolve({ type: 'success', @@ -26,7 +26,7 @@ export class TextSearchEngineAdapter { } const pretendOutputChannel = { - appendLine(msg) { + appendLine(msg: string) { onMessage({ message: msg }); } }; diff --git a/src/vs/workbench/services/search/test/node/rawSearchService.test.ts b/src/vs/workbench/services/search/test/node/rawSearchService.test.ts index 76b2b732d6..68c517c3cf 100644 --- a/src/vs/workbench/services/search/test/node/rawSearchService.test.ts +++ b/src/vs/workbench/services/search/test/node/rawSearchService.test.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import * as path from 'vs/base/common/path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; +import * as path from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; -import { IFileQuery, IFileSearchStats, IFolderQuery, IProgress, ISearchEngineStats, QueryType, IRawFileMatch, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from 'vs/workbench/services/search/common/search'; -import { SearchService as RawSearchService } from 'vs/workbench/services/search/node/rawSearchService'; +import { IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, IRawFileMatch, ISearchEngine, ISearchEngineStats, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess, QueryType } from 'vs/workbench/services/search/common/search'; +import { IProgressCallback, SearchService as RawSearchService } from 'vs/workbench/services/search/node/rawSearchService'; import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; const TEST_FOLDER_QUERIES = [ @@ -40,7 +40,7 @@ class TestSearchEngine implements ISearchEngine { TestSearchEngine.last = this; } - search(onResult: (match: IRawFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISearchEngineSuccess) => void): void { + search(onResult: (match: IRawFileMatch) => void, onProgress: (progress: IProgressMessage) => void, done: (error: Error, complete: ISearchEngineSuccess) => void): void { const self = this; (function next() { process.nextTick(() => { @@ -157,7 +157,7 @@ suite('RawSearchService', () => { } const progressResults: any[] = []; - const onProgress = match => { + const onProgress = (match: IFileMatch) => { assert.strictEqual(match.resource.path, uriPath); progressResults.push(match); }; @@ -217,7 +217,7 @@ suite('RawSearchService', () => { const service = new RawSearchService(); const results: any[] = []; - const cb = value => { + const cb: IProgressCallback = value => { if (Array.isArray(value)) { results.push(...value.map(v => v.path)); } else { @@ -243,7 +243,7 @@ suite('RawSearchService', () => { const service = new RawSearchService(); const results: number[] = []; - const cb = value => { + const cb: IProgressCallback = value => { if (Array.isArray(value)) { value.forEach(m => { assert.deepStrictEqual(m, match); @@ -276,7 +276,7 @@ suite('RawSearchService', () => { const service = new RawSearchService(); const results: any[] = []; - const cb = value => { + const cb: IProgressCallback = value => { if (Array.isArray(value)) { results.push(...value.map(v => v.path)); } else { @@ -294,7 +294,7 @@ suite('RawSearchService', () => { assert.deepStrictEqual(results, [path.normalize('/some/where/bcb'), path.normalize('/some/where/bbc'), path.normalize('/some/where/aab')]); }).then(async () => { const results: any[] = []; - const cb = value => { + const cb: IProgressCallback = value => { if (Array.isArray(value)) { results.push(...value.map(v => v.path)); } else { @@ -323,7 +323,7 @@ suite('RawSearchService', () => { size: 3 }); const results: any[] = []; - const cb = value => { + const cb: IProgressCallback = value => { if (Array.isArray(value)) { results.push(...value.map(v => v.path)); } else { diff --git a/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts b/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts index b352a166b3..159fa33324 100644 --- a/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts +++ b/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts @@ -42,7 +42,7 @@ suite('RipgrepTextSearchEngine', () => { }); test('fixRegexCRMatchingWhitespaceClass', () => { - function testFixRegexCRMatchingWhitespaceClass([inputReg, isMultiline, testStr, shouldMatch]): void { + function testFixRegexCRMatchingWhitespaceClass([inputReg, isMultiline, testStr, shouldMatch]: [string, boolean, string, boolean]): void { const fixed = fixRegexCRMatchingWhitespaceClass(inputReg, isMultiline); const reg = new RegExp(fixed); assert.equal(reg.test(testStr), shouldMatch, `${inputReg} => ${reg}, ${testStr}, ${shouldMatch}`); @@ -66,7 +66,7 @@ suite('RipgrepTextSearchEngine', () => { }); test('fixRegexCRMatchingNonWordClass', () => { - function testRegexCRMatchingNonWordClass([inputReg, isMultiline, testStr, shouldMatch]): void { + function testRegexCRMatchingNonWordClass([inputReg, isMultiline, testStr, shouldMatch]: [string, boolean, string, boolean]): void { const fixed = fixRegexCRMatchingNonWordClass(inputReg, isMultiline); const reg = new RegExp(fixed); assert.equal(reg.test(testStr), shouldMatch, `${inputReg} => ${reg}, ${testStr}, ${shouldMatch}`); @@ -90,7 +90,7 @@ suite('RipgrepTextSearchEngine', () => { }); test('fixRegexNewline', () => { - function testFixRegexNewline([inputReg, testStr, shouldMatch]): void { + function testFixRegexNewline([inputReg, testStr, shouldMatch]: [string, string, boolean]): void { const fixed = fixRegexNewline(inputReg); const reg = new RegExp(fixed); assert.equal(reg.test(testStr), shouldMatch, `${inputReg} => ${reg}, ${testStr}, ${shouldMatch}`); @@ -111,7 +111,7 @@ suite('RipgrepTextSearchEngine', () => { }); test('fixNewline', () => { - function testFixNewline([inputReg, testStr, shouldMatch = true]): void { + function testFixNewline([inputReg, testStr, shouldMatch = true]: [string, string, boolean]): void { const fixed = fixNewline(inputReg); const reg = new RegExp(fixed); assert.equal(reg.test(testStr), shouldMatch, `${inputReg} => ${reg}, ${testStr}, ${shouldMatch}`); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 744974a7fb..6020ed90e0 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -9,14 +9,14 @@ import { Event, Emitter } from 'vs/base/common/event'; import { guessMimeTypes } from 'vs/base/common/mime'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { URI } from 'vs/base/common/uri'; -import { isUndefinedOrNull } from 'vs/base/common/types'; +import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, ISaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, IRawTextContent, ILoadOptions, LoadReason, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { EncodingMode } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { IFileService, IFileStat, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; +import { IFileService, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType, IFileStatWithMetadata, etag } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -60,7 +60,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private dirty: boolean; private versionId: number; private bufferSavedVersionId: number; - private lastResolvedDiskStat: IFileStat; + private lastResolvedDiskStat: IFileStatWithMetadata; private blockModelContentChange: boolean; private autoSaveAfterMillies?: number; private autoSaveAfterMilliesEnabled: boolean; @@ -270,7 +270,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil resource: this.resource, name: basename(this.resource), mtime: Date.now(), - etag: undefined, + size: 0, + etag: etag(Date.now(), 0), value: createTextBufferFactory(''), /* will be filled later from backup */ encoding: this.fileService.encoding.getWriteEncoding(this.resource, this.preferredEncoding).encoding, isReadonly: false @@ -349,6 +350,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private loadWithContent(content: IRawTextContent, options?: ILoadOptions, backup?: URI): Promise { return this.doLoadWithContent(content, backup).then(model => { + // Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype const settingsType = this.getTypeIfSettings(); if (settingsType) { @@ -381,12 +383,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil resource: this.resource, name: content.name, mtime: content.mtime, + size: content.size, etag: content.etag, isDirectory: false, isSymbolicLink: false, - children: undefined, isReadonly: content.isReadonly - } as IFileStat); + }); // Keep the original encoding to not loose it when saving const oldEncoding = this.contentEncoding; @@ -490,7 +492,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return Promise.resolve(null); } - return this.backupFileService.resolveBackupContent(backup).then(backupContent => backupContent || null, error => null /* ignore errors */); + return this.backupFileService.resolveBackupContent(backup).then(withUndefinedAsNull, error => null /* ignore errors */); } protected getOrCreateMode(modeService: IModeService, preferredModeIds: string | undefined, firstLineText?: string): ILanguageSelection { @@ -901,7 +903,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - private updateLastResolvedDiskStat(newVersionOnDiskStat: IFileStat): void { + private updateLastResolvedDiskStat(newVersionOnDiskStat: IFileStatWithMetadata): void { // First resolve - just take if (!this.lastResolvedDiskStat) { @@ -1036,7 +1038,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.resource; } - getStat(): IFileStat { + getStat(): IFileStatWithMetadata { return this.lastResolvedDiskStat; } diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 3688c1c178..58d325fe2f 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -109,16 +109,16 @@ export class TextFileService extends Disposable implements ITextFileService { resolveTextContent(resource: URI, options?: IResolveContentOptions): Promise { return this.fileService.resolveStreamContent(resource, options).then(streamContent => { return createTextBufferFactoryFromStream(streamContent.value).then(res => { - const r: IRawTextContent = { + return { resource: streamContent.resource, name: streamContent.name, mtime: streamContent.mtime, etag: streamContent.etag, encoding: streamContent.encoding, isReadonly: streamContent.isReadonly, + size: streamContent.size, value: res - }; - return r; + } as IRawTextContent; }); }); } diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 2cc26a0dbd..f21d68ddc7 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -7,7 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IEncodingSupport, ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor'; -import { IBaseStat, IResolveContentOptions, ITextSnapshot } from 'vs/platform/files/common/files'; +import { IResolveContentOptions, ITextSnapshot, IBaseStatWithMetadata } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; import { ITextBufferFactory, ITextModel } from 'vs/editor/common/model'; @@ -129,7 +129,7 @@ export const enum LoadReason { export const ITextFileService = createDecorator(TEXT_FILE_SERVICE_ID); -export interface IRawTextContent extends IBaseStat { +export interface IRawTextContent extends IBaseStatWithMetadata { /** * The line grouped content of a text file. diff --git a/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts b/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts index 5354b31d27..d68fdef07f 100644 --- a/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts +++ b/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts @@ -7,12 +7,12 @@ import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { OperatingSystem, OS } from 'vs/base/common/platform'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { Schemas } from 'vs/base/common/network'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; export class TextResourcePropertiesService implements ITextResourcePropertiesService { @@ -26,10 +26,7 @@ export class TextResourcePropertiesService implements ITextResourcePropertiesSer @IWindowService private readonly windowService: IWindowService, @IStorageService private readonly storageService: IStorageService ) { - const remoteAgentConnection = remoteAgentService.getConnection(); - if (remoteAgentConnection) { - remoteAgentConnection.getEnvironment().then(remoteEnv => this.remoteEnvironment = remoteEnv); - } + remoteAgentService.getEnvironment().then(remoteEnv => this.remoteEnvironment = remoteEnv); } getEOL(resource: URI, language?: string): string { diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index 41c75d660f..5a42125d73 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -561,7 +561,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('Parameter Hints, back and forth', async () => { disposables.push(extHost.registerSignatureHelpProvider(nullExtensionDescription, defaultSelector, new class implements vscode.SignatureHelpProvider { - provideSignatureHelp(_document, _position, _token, context: vscode.SignatureHelpContext): vscode.SignatureHelp { + provideSignatureHelp(_document: vscode.TextDocument, _position: vscode.Position, _token: vscode.CancellationToken, context: vscode.SignatureHelpContext): vscode.SignatureHelp { return { activeSignature: 0, activeParameter: 1, diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index 5c027f2002..0bd4fa521a 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -33,7 +33,7 @@ import { getWorkspaceSymbols } from 'vs/workbench/contrib/search/common/search'; import { rename } from 'vs/editor/contrib/rename/rename'; import { provideSignatureHelp } from 'vs/editor/contrib/parameterHints/provideSignatureHelp'; import { provideSuggestionItems, CompletionOptions } from 'vs/editor/contrib/suggest/suggest'; -import { getDocumentFormattingEdits, getDocumentRangeFormattingEdits, getOnTypeFormattingEdits, FormatMode } from 'vs/editor/contrib/format/format'; +import { getDocumentFormattingEditsUntilResult, getDocumentRangeFormattingEditsUntilResult, getOnTypeFormattingEdits } from 'vs/editor/contrib/format/format'; import { getLinks } from 'vs/editor/contrib/links/getLinks'; import { MainContext, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics'; @@ -46,10 +46,10 @@ import { getColors } from 'vs/editor/contrib/colorPicker/color'; import { CancellationToken } from 'vs/base/common/cancellation'; import { nullExtensionDescription as defaultExtension } from 'vs/workbench/services/extensions/common/extensions'; import { provideSelectionRanges } from 'vs/editor/contrib/smartSelect/smartSelect'; -import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { dispose } from 'vs/base/common/lifecycle'; +import { withNullAsUndefined } from 'vs/base/common/types'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = EditorModel.createFromString( @@ -906,8 +906,8 @@ suite('ExtHostLanguageFeatures', function () { // --- format const NullWorkerService = new class extends mock() { - computeMoreMinimalEdits(resource: URI, edits: modes.TextEdit[] | null | undefined): Promise { - return Promise.resolve(edits); + computeMoreMinimalEdits(resource: URI, edits: modes.TextEdit[] | null | undefined): Promise { + return Promise.resolve(withNullAsUndefined(edits)); } }; @@ -919,7 +919,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - let value = (await getDocumentFormattingEdits(NullTelemetryService, NullWorkerService, model, { insertSpaces: true, tabSize: 4 }, FormatMode.Auto, CancellationToken.None))!; + let value = (await getDocumentFormattingEditsUntilResult(NullWorkerService, model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None))!; assert.equal(value.length, 2); let [first, second] = value; assert.equal(first.text, 'testing'); @@ -937,7 +937,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - return getDocumentFormattingEdits(NullTelemetryService, NullWorkerService, model, { insertSpaces: true, tabSize: 4 }, FormatMode.Auto, CancellationToken.None); + return getDocumentFormattingEditsUntilResult(NullWorkerService, model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None); }); test('Format Doc, order', async () => { @@ -961,7 +961,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - let value = (await getDocumentFormattingEdits(NullTelemetryService, NullWorkerService, model, { insertSpaces: true, tabSize: 4 }, FormatMode.Auto, CancellationToken.None))!; + let value = (await getDocumentFormattingEditsUntilResult(NullWorkerService, model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None))!; assert.equal(value.length, 1); let [first] = value; assert.equal(first.text, 'testing'); @@ -976,7 +976,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const value = (await getDocumentRangeFormattingEdits(NullTelemetryService, NullWorkerService, model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, FormatMode.Auto, CancellationToken.None))!; + const value = (await getDocumentRangeFormattingEditsUntilResult(NullWorkerService, model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None))!; assert.equal(value.length, 1); const [first] = value; assert.equal(first.text, 'testing'); @@ -1000,7 +1000,7 @@ suite('ExtHostLanguageFeatures', function () { } })); await rpcProtocol.sync(); - const value = (await getDocumentRangeFormattingEdits(NullTelemetryService, NullWorkerService, model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, FormatMode.Auto, CancellationToken.None))!; + const value = (await getDocumentRangeFormattingEditsUntilResult(NullWorkerService, model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None))!; assert.equal(value.length, 1); const [first] = value; assert.equal(first.text, 'range2'); @@ -1018,7 +1018,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - return getDocumentRangeFormattingEdits(NullTelemetryService, NullWorkerService, model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, FormatMode.Auto, CancellationToken.None); + return getDocumentRangeFormattingEditsUntilResult(NullWorkerService, model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None); }); test('Format on Type, data conversion', async () => { @@ -1030,7 +1030,7 @@ suite('ExtHostLanguageFeatures', function () { }, [';'])); await rpcProtocol.sync(); - const value = (await getOnTypeFormattingEdits(NullTelemetryService, NullWorkerService, model, new EditorPosition(1, 1), ';', { insertSpaces: true, tabSize: 2 }))!; + const value = (await getOnTypeFormattingEdits(NullWorkerService, model, new EditorPosition(1, 1), ';', { insertSpaces: true, tabSize: 2 }))!; assert.equal(value.length, 1); const [first] = value; assert.equal(first.text, ';'); diff --git a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts index ded16713f2..102eede466 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts @@ -90,7 +90,7 @@ suite('ExtHostMessageService', function () { suite('modal', () => { test('calls dialog service', async () => { const service = new MainThreadMessageService(null!, emptyNotificationService, emptyCommandService, new class extends mock() { - show(severity, message, buttons) { + show(severity: Severity, message: string, buttons: string[]) { assert.equal(severity, 1); assert.equal(message, 'h'); assert.equal(buttons.length, 2); @@ -105,7 +105,7 @@ suite('ExtHostMessageService', function () { test('returns undefined when cancelled', async () => { const service = new MainThreadMessageService(null!, emptyNotificationService, emptyCommandService, new class extends mock() { - show(severity, message, buttons) { + show() { return Promise.resolve(1); } } as IDialogService); @@ -116,7 +116,7 @@ suite('ExtHostMessageService', function () { test('hides Cancel button when not needed', async () => { const service = new MainThreadMessageService(null!, emptyNotificationService, emptyCommandService, new class extends mock() { - show(severity, message, buttons) { + show(severity: Severity, message: string, buttons: string[]) { assert.equal(buttons.length, 1); return Promise.resolve(0); } diff --git a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts index 8cc5b14b6f..55dd92ed9b 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts @@ -43,7 +43,9 @@ suite('ExtHostTreeView', function () { let target: RecordingShape; let onDidChangeTreeNode: Emitter<{ key: string } | undefined>; let onDidChangeTreeNodeWithId: Emitter<{ key: string }>; - let tree, labels, nodes; + let tree: object; + let labels: object; + let nodes: object; setup(() => { tree = { @@ -582,7 +584,7 @@ suite('ExtHostTreeView', function () { }); }); - function loadCompleteTree(treeId, element?: string) { + function loadCompleteTree(treeId: string, element?: string): Promise { return testObject.$getChildren(treeId, element) .then(elements => elements.map(e => loadCompleteTree(treeId, e.handle))) .then(() => null); @@ -661,7 +663,7 @@ suite('ExtHostTreeView', function () { }; } - function getTreeElement(element): any { + function getTreeElement(element: string): any { let parent = tree; for (let i = 0; i < element.length; i++) { parent = parent[element.substring(0, i + 1)]; diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadDocumentContentProviders.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadDocumentContentProviders.test.ts index 1ae36a493b..a4ff357116 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadDocumentContentProviders.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadDocumentContentProviders.test.ts @@ -11,6 +11,7 @@ import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol'; +import { TextEdit } from 'vs/editor/common/modes'; suite('MainThreadDocumentContentProviders', function () { @@ -21,13 +22,13 @@ suite('MainThreadDocumentContentProviders', function () { let providers = new MainThreadDocumentContentProviders(new TestRPCProtocol(), null!, null!, new class extends mock() { - getModel(_uri) { + getModel(_uri: URI) { assert.equal(uri.toString(), _uri.toString()); return model; } }, new class extends mock() { - computeMoreMinimalEdits(_uri, data) { + computeMoreMinimalEdits(_uri: URI, data: TextEdit[] | undefined) { assert.equal(model.getValue(), '1'); return Promise.resolve(data); } diff --git a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts index 28b820a536..9c2f0f6318 100644 --- a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts @@ -97,15 +97,15 @@ suite.skip('TextSearch performance (integration)', () => { telemetryService.events = []; - resolve(resultsFinishedEvent); + resolve!(resultsFinishedEvent); } catch (e) { // Fail the runSearch() promise - error(e); + error!(e); } } - let resolve; - let error; + let resolve: (result: any) => void; + let error: (error: Error) => void; return new Promise((_resolve, _error) => { resolve = _resolve; error = _error; @@ -122,9 +122,9 @@ suite.skip('TextSearch performance (integration)', () => { .then(() => { if (testWorkspaceArg) { // Don't measure by default let i = n; - return (function iterate() { + return (function iterate(): Promise | undefined { if (!i--) { - return; + return undefined; } return runSearch() @@ -133,11 +133,12 @@ suite.skip('TextSearch performance (integration)', () => { finishedEvents.push(resultsFinishedEvent); return iterate(); }); - })().then(() => { + })()!.then(() => { const totalTime = finishedEvents.reduce((sum, e) => sum + e.data.duration, 0); console.log(`Avg duration: ${totalTime / n / 1000}s`); }); } + return undefined; }); }); }); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index a9a183cb94..07bbae0942 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -26,7 +26,7 @@ import { IWorkspaceContextService, IWorkspace as IWorkbenchWorkspace, WorkbenchS import { ILifecycleService, BeforeShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService'; -import { FileOperationEvent, IFileService, IResolveContentOptions, FileOperationError, IFileStat, IResolveFileResult, FileChangesEvent, IResolveFileOptions, IContent, IUpdateContentOptions, IStreamContent, ICreateFileOptions, ITextSnapshot, IResourceEncodings, IResourceEncoding, IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions } from 'vs/platform/files/common/files'; +import { FileOperationEvent, IFileService, IResolveContentOptions, FileOperationError, IFileStat, IResolveFileResult, FileChangesEvent, IResolveFileOptions, IContent, IUpdateContentOptions, IStreamContent, ICreateFileOptions, ITextSnapshot, IResourceEncodings, IResourceEncoding, IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions, IFileStatWithMetadata, IResolveMetadataFileOptions } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; @@ -251,7 +251,8 @@ export class TestTextFileService extends TextFileService { mtime: content.mtime, etag: content.etag, encoding: content.encoding, - value: createTextBufferFactory(content.value) + value: createTextBufferFactory(content.value), + size: content.value.length }; }); } @@ -505,10 +506,6 @@ export class TestLayoutService implements IWorkbenchLayoutService { return false; } - public get hasWorkbench(): boolean { - return true; - } - public setEditorHidden(_hidden: boolean): Promise { return Promise.resolve(); } public setSideBarHidden(_hidden: boolean): Promise { return Promise.resolve(); } @@ -932,12 +929,15 @@ export class TestFileService implements IFileService { this._onAfterOperation.fire(event); } + resolveFile(resource: URI, _options?: IResolveFileOptions): Promise; + resolveFile(resource: URI, _options: IResolveMetadataFileOptions): Promise; resolveFile(resource: URI, _options?: IResolveFileOptions): Promise { return Promise.resolve({ resource, etag: Date.now().toString(), encoding: 'utf8', mtime: Date.now(), + size: 42, isDirectory: false, name: resources.basename(resource) }); @@ -958,7 +958,8 @@ export class TestFileService implements IFileService { etag: 'index.txt', encoding: 'utf8', mtime: Date.now(), - name: resources.basename(resource) + name: resources.basename(resource), + size: 1 }); } @@ -978,34 +979,36 @@ export class TestFileService implements IFileService { etag: 'index.txt', encoding: 'utf8', mtime: Date.now(), + size: 1, name: resources.basename(resource) }); } - updateContent(resource: URI, _value: string | ITextSnapshot, _options?: IUpdateContentOptions): Promise { + updateContent(resource: URI, _value: string | ITextSnapshot, _options?: IUpdateContentOptions): Promise { return timeout(0).then(() => ({ resource, etag: 'index.txt', encoding: 'utf8', mtime: Date.now(), + size: 42, isDirectory: false, name: resources.basename(resource) })); } - moveFile(_source: URI, _target: URI, _overwrite?: boolean): Promise { + moveFile(_source: URI, _target: URI, _overwrite?: boolean): Promise { return Promise.resolve(null!); } - copyFile(_source: URI, _target: URI, _overwrite?: boolean): Promise { + copyFile(_source: URI, _target: URI, _overwrite?: boolean): Promise { throw new Error('not implemented'); } - createFile(_resource: URI, _content?: string, _options?: ICreateFileOptions): Promise { + createFile(_resource: URI, _content?: string, _options?: ICreateFileOptions): Promise { throw new Error('not implemented'); } - createFolder(_resource: URI): Promise { + createFolder(_resource: URI): Promise { throw new Error('not implemented'); } diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index a7551bd518..1cf2a1056b 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -74,8 +74,6 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { LocalizationsService } from 'vs/platform/localizations/electron-browser/localizationsService'; import { ISharedProcessService, SharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService'; -import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { TelemetryService } from 'vs/platform/telemetry/electron-browser/telemetryService'; import { IProductService } from 'vs/platform/product/common/product'; @@ -133,7 +131,6 @@ import 'vs/workbench/services/label/common/labelService'; import 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService'; import 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import 'vs/workbench/services/notification/common/notificationService'; -import 'vs/workbench/services/remote/common/remoteEnvironmentService'; import 'vs/workbench/services/heap/node/heap'; registerSingleton(IMenuService, MenuService, true); @@ -155,7 +152,6 @@ registerSingleton(IRequestService, RequestService, true); registerSingleton(ILifecycleService, LifecycleService); registerSingleton(ILocalizationsService, LocalizationsService); registerSingleton(ISharedProcessService, SharedProcessService, true); -registerSingleton(IRemoteAuthorityResolverService, RemoteAuthorityResolverService, true); registerSingleton(ITelemetryService, TelemetryService); registerSingleton(IProductService, ProductService, true); registerSingleton(IWindowsService, WindowsService); diff --git a/src/vs/workbench/workbench.nodeless.main.ts b/src/vs/workbench/workbench.nodeless.main.ts index 7619d67871..c307f39635 100644 --- a/src/vs/workbench/workbench.nodeless.main.ts +++ b/src/vs/workbench/workbench.nodeless.main.ts @@ -137,7 +137,7 @@ import 'vs/workbench/services/label/common/labelService'; // import 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService'; // import 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import 'vs/workbench/services/notification/common/notificationService'; -import 'vs/workbench/services/remote/common/remoteEnvironmentService'; + @@ -160,7 +160,6 @@ registerSingleton(IContextViewService, ContextViewService, true); // registerSingleton(ILifecycleService, LifecycleService); // registerSingleton(ILocalizationsService, LocalizationsService); // registerSingleton(ISharedProcessService, SharedProcessService, true); -// registerSingleton(IRemoteAuthorityResolverService, RemoteAuthorityResolverService, true); // registerSingleton(ITelemetryService, TelemetryService); // registerSingleton(IProductService, ProductService, true); // registerSingleton(IWindowsService, WindowsService);