diff --git a/.vscode/launch.json b/.vscode/launch.json index 73b99e84fbb..a117e3ed45d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -147,7 +147,7 @@ }, "urlFilter": "*workbench.html*", "runtimeArgs": [ - "--inspect=5875" + "--inspect=5875", "--no-cached-data" ], "smartStep": true, "skipFiles": [ @@ -155,6 +155,19 @@ ], "webRoot": "${workspaceFolder}" }, + { + "type": "node", + "request": "launch", + "name": "Launch VS Code (Main Process)", + "runtimeExecutable": "${workspaceFolder}/scripts/code.sh", + "runtimeArgs": [ + "--no-cached-data" + ], + "smartStep": true, + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ] + }, { "type": "node", "request": "launch", diff --git a/build/tfs/darwin/continuous-build-darwin.yml b/build/tfs/darwin/continuous-build-darwin.yml index 28e2a6079ab..99e17e940a6 100644 --- a/build/tfs/darwin/continuous-build-darwin.yml +++ b/build/tfs/darwin/continuous-build-darwin.yml @@ -32,17 +32,6 @@ steps: - script: | ./scripts/test-integration.sh --tfs "Integration Tests" displayName: Run Integration Tests -- script: | - yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)/artifacts" --log "$(Build.ArtifactStagingDirectory)/artifacts/smoketest.log" - displayName: Run Smoke Tests - continueOnError: true -- task: PublishBuildArtifacts@1 - displayName: Publish Smoketest Artifacts - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' - ArtifactName: build-artifacts-darwin - publishLocation: Container - condition: eq(variables['System.PullRequest.IsFork'], 'False') - task: PublishTestResults@2 displayName: Publish Tests Results inputs: diff --git a/build/tfs/win32/continuous-build-win32.yml b/build/tfs/win32/continuous-build-win32.yml index 0df397a0a6a..7145e67e2ad 100644 --- a/build/tfs/win32/continuous-build-win32.yml +++ b/build/tfs/win32/continuous-build-win32.yml @@ -36,17 +36,6 @@ steps: - powershell: | .\scripts\test-integration.bat --tfs "Integration Tests" displayName: Run Integration Tests -- powershell: | - yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)\artifacts" --log "$(Build.ArtifactStagingDirectory)\artifacts\smoketest.log" - displayName: Run Smoke Tests - continueOnError: true -- task: PublishBuildArtifacts@1 - displayName: Publish Smoketest Artifacts - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' - ArtifactName: build-artifacts-win32 - publishLocation: Container - condition: eq(variables['System.PullRequest.IsFork'], 'False') - task: PublishTestResults@2 displayName: Publish Tests Results inputs: diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index c779f246284..069e7929d1a 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -143,7 +143,7 @@ "error" ], "default": "warning", - "description": "%css.lint.fontFaceProperties.desc%" + "markdownDescription": "%css.lint.fontFaceProperties.desc%" }, "css.lint.hexColorLength": { "type": "string", @@ -220,7 +220,7 @@ "error" ], "default": "ignore", - "description": "%css.lint.important.desc%" + "markdownDescription": "%css.lint.important.desc%" }, "css.lint.float": { "type": "string", @@ -679,7 +679,7 @@ "error" ], "default": "ignore", - "description": "%less.lint.important.desc%" + "markdownDescription": "%less.lint.important.desc%" }, "less.lint.float": { "type": "string", diff --git a/extensions/css-language-features/package.nls.json b/extensions/css-language-features/package.nls.json index f62c6fb669c..7c344718b8c 100644 --- a/extensions/css-language-features/package.nls.json +++ b/extensions/css-language-features/package.nls.json @@ -35,7 +35,7 @@ "less.lint.hexColorLength.desc": "Hex colors must consist of three or six hex numbers.", "less.lint.idSelector.desc": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML.", "less.lint.ieHack.desc": "IE hacks are only necessary when supporting IE7 and older.", - "less.lint.important.desc": "Avoid using !important. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.", + "less.lint.important.desc": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.", "less.lint.importStatement.desc": "Import statements do not load in parallel.", "less.lint.propertyIgnoredDueToDisplay.desc": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.", "less.lint.universalSelector.desc": "The universal selector (`*`) is known to be slow.", @@ -56,7 +56,7 @@ "scss.lint.hexColorLength.desc": "Hex colors must consist of three or six hex numbers.", "scss.lint.idSelector.desc": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML.", "scss.lint.ieHack.desc": "IE hacks are only necessary when supporting IE7 and older.", - "scss.lint.important.desc": "Avoid using !important. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.", + "scss.lint.important.desc": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.", "scss.lint.importStatement.desc": "Import statements do not load in parallel.", "scss.lint.propertyIgnoredDueToDisplay.desc": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.", "scss.lint.universalSelector.desc": "The universal selector (`*`) is known to be slow.", diff --git a/extensions/javascript/snippets/javascript.json b/extensions/javascript/snippets/javascript.json index 5579e4e0c16..5da4ebe0c18 100644 --- a/extensions/javascript/snippets/javascript.json +++ b/extensions/javascript/snippets/javascript.json @@ -140,6 +140,15 @@ ], "description": "Set Timeout Function" }, + "Set Interval Function": { + "prefix": "setinterval", + "body": [ + "setInterval(() => {", + "\t$0", + "}, ${1:interval});" + ], + "description": "Set Interval Function" + }, "Import external module.": { "prefix": "import statement", "body": [ diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 085b1a5e2eb..f381a0c0ae7 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -46,6 +46,9 @@ export class MarkdownEngine { if (lang && lang.toLocaleLowerCase() === 'json5') { lang = 'json'; } + if (lang && lang.toLocaleLowerCase() === 'c#') { + lang = 'cs'; + } if (lang && hljs.getLanguage(lang)) { try { return `
${hljs.highlight(lang, str, true).value}
`; diff --git a/package.json b/package.json index d362ad36c35..80f1b279dc7 100644 --- a/package.json +++ b/package.json @@ -49,10 +49,10 @@ "vscode-chokidar": "1.6.4", "vscode-debugprotocol": "1.32.0", "vscode-nsfw": "1.1.1", - "vscode-ripgrep": "^1.2.2", + "vscode-ripgrep": "^1.2.4", "vscode-sqlite3": "4.0.2", "vscode-textmate": "^4.0.1", - "vscode-xterm": "3.9.0-beta9", + "vscode-xterm": "3.9.0-beta11", "winreg": "^1.2.4", "yauzl": "^2.9.1", "yazl": "^2.4.3" diff --git a/scripts/code.bat b/scripts/code.bat index 58d5ca2d6e0..6789e54fdda 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -17,7 +17,7 @@ set CODE=".build\electron\%NAMESHORT%" node build\lib\electron.js if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron -:: Manage build-in extensions +:: Manage built-in extensions if "%1"=="--builtin" goto builtin :: Sync built-in extensions @@ -49,4 +49,4 @@ goto end popd -endlocal \ No newline at end of file +endlocal diff --git a/src/main.js b/src/main.js index 92375bc68c0..bc430b9c672 100644 --- a/src/main.js +++ b/src/main.js @@ -88,10 +88,7 @@ function onReady() { const startup = nlsConfig => { nlsConfig._languagePackSupport = true; process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig); - - if (cachedDataDir) { - process.env['VSCODE_NODE_CACHED_DATA_DIR'] = cachedDataDir; - } + process.env['VSCODE_NODE_CACHED_DATA_DIR'] = cachedDataDir || ''; // Load main in AMD require('./bootstrap-amd').load('vs/code/electron-main/main'); diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 766b4d9b2eb..1693a2a6a02 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -22,11 +22,18 @@ "./vs/base/browser/mouseEvent.ts", "./vs/base/browser/touch.ts", "./vs/base/browser/ui/aria/aria.ts", + "./vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts", "./vs/base/browser/ui/button/button.ts", + "./vs/base/browser/ui/centered/centeredViewLayout.ts", "./vs/base/browser/ui/contextview/contextview.ts", "./vs/base/browser/ui/countBadge/countBadge.ts", + "./vs/base/browser/ui/grid/gridview.ts", + "./vs/base/browser/ui/highlightedlabel/highlightedLabel.ts", + "./vs/base/browser/ui/iconLabel/iconLabel.ts", "./vs/base/browser/ui/keybindingLabel/keybindingLabel.ts", "./vs/base/browser/ui/list/list.ts", + "./vs/base/browser/ui/list/rangeMap.ts", + "./vs/base/browser/ui/list/rowCache.ts", "./vs/base/browser/ui/list/splice.ts", "./vs/base/browser/ui/octiconLabel/octiconLabel.mock.ts", "./vs/base/browser/ui/octiconLabel/octiconLabel.ts", @@ -40,9 +47,13 @@ "./vs/base/browser/ui/scrollbar/scrollbarState.ts", "./vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts", "./vs/base/browser/ui/scrollbar/verticalScrollbar.ts", + "./vs/base/browser/ui/splitview/panelview.ts", "./vs/base/browser/ui/splitview/splitview.ts", "./vs/base/browser/ui/tree/tree.ts", "./vs/base/browser/ui/widget.ts", + "./vs/base/common/json.ts", + "./vs/base/common/jsonEdit.ts", + "./vs/base/common/jsonFormatter.ts", "./vs/base/node/console.ts", "./vs/base/node/crypto.ts", "./vs/base/node/decoder.ts", @@ -62,6 +73,10 @@ "./vs/base/parts/ipc/node/ipc.ts", "./vs/base/parts/ipc/test/node/testService.ts", "./vs/base/parts/quickopen/common/quickOpen.ts", + "./vs/base/test/browser/ui/grid/util.ts", + "./vs/base/test/common/json.test.ts", + "./vs/base/test/common/jsonEdit.test.ts", + "./vs/base/test/common/jsonFormatter.test.ts", "./vs/base/test/common/utils.ts", "./vs/base/test/node/processes/fixtures/fork.ts", "./vs/base/test/node/processes/fixtures/fork_large.ts", @@ -238,6 +253,7 @@ "./vs/editor/contrib/codeAction/codeAction.ts", "./vs/editor/contrib/codeAction/codeActionTrigger.ts", "./vs/editor/contrib/colorPicker/color.ts", + "./vs/editor/contrib/colorPicker/colorDetector.ts", "./vs/editor/contrib/colorPicker/colorPickerModel.ts", "./vs/editor/contrib/comment/blockCommentCommand.ts", "./vs/editor/contrib/comment/comment.ts", @@ -393,7 +409,9 @@ "./vs/platform/quickOpen/common/quickOpen.ts", "./vs/platform/quickinput/common/quickInput.ts", "./vs/platform/registry/common/platform.ts", + "./vs/platform/request/electron-main/requestService.ts", "./vs/platform/request/node/request.ts", + "./vs/platform/request/node/requestService.ts", "./vs/platform/search/common/search.ts", "./vs/platform/state/common/state.ts", "./vs/platform/statusbar/common/statusbar.ts", @@ -470,6 +488,7 @@ "./vs/workbench/parts/scm/electron-browser/scmUtil.ts", "./vs/workbench/parts/search/common/constants.ts", "./vs/workbench/parts/surveys/electron-browser/nps.contribution.ts", + "./vs/workbench/parts/tasks/common/problemMatcher.ts", "./vs/workbench/parts/tasks/common/taskTemplates.ts", "./vs/workbench/parts/terminal/browser/terminalWidgetManager.ts", "./vs/workbench/parts/terminal/common/terminal.ts", diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 3ad8440553d..38bb0c0d511 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -560,7 +560,7 @@ export class Dimension { this.height = height; } - static equals(a: Dimension, b: Dimension): boolean { + static equals(a: Dimension | undefined, b: Dimension | undefined): boolean { if (a === b) { return true; } diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 4a2b7396a9e..42f43d27322 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -118,8 +118,8 @@ export class BreadcrumbsWidget { this._freeNodes.length = 0; } - layout(dim: dom.Dimension): void { - if (dom.Dimension.equals(dim, this._dimension)) { + layout(dim: dom.Dimension | undefined): void { + if (dim && dom.Dimension.equals(dim, this._dimension)) { return; } if (this._pendingLayout) { @@ -146,9 +146,11 @@ export class BreadcrumbsWidget { private _updateScrollbar(): IDisposable { return dom.measure(() => { - this._scrollable.setRevealOnScroll(false); - this._scrollable.scanDomNode(); - this._scrollable.setRevealOnScroll(true); + dom.measure(() => { // double RAF + this._scrollable.setRevealOnScroll(false); + this._scrollable.scanDomNode(); + this._scrollable.setRevealOnScroll(true); + }); }); } @@ -277,8 +279,8 @@ export class BreadcrumbsWidget { } setItems(items: BreadcrumbsItem[]): void { - let prefix: number; - let removed: BreadcrumbsItem[]; + let prefix: number | undefined; + let removed: BreadcrumbsItem[] = []; try { prefix = commonPrefixLength(this._items, items, (a, b) => a.equals(b)); removed = this._items.splice(prefix, this._items.length - prefix, ...items.slice(prefix)); @@ -302,17 +304,21 @@ export class BreadcrumbsWidget { // case a: more nodes -> remove them while (start < this._nodes.length) { const free = this._nodes.pop(); - this._freeNodes.push(free); - free.remove(); + if (free) { + this._freeNodes.push(free); + free.remove(); + } } // case b: more items -> render them for (; start < this._items.length; start++) { let item = this._items[start]; let node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('div'); - this._renderItem(item, node); - this._domNode.appendChild(node); - this._nodes.push(node); + if (node) { + this._renderItem(item, node); + this._domNode.appendChild(node); + this._nodes.push(node); + } } this.layout(undefined); } @@ -327,7 +333,7 @@ export class BreadcrumbsWidget { } private _onClick(event: IMouseEvent): void { - for (let el = event.target; el; el = el.parentElement) { + for (let el: HTMLElement | null = event.target; el; el = el.parentElement) { let idx = this._nodes.indexOf(el as any); if (idx >= 0) { this._focus(idx, event); diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 03b876df89b..0da0a7f3b11 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -50,12 +50,12 @@ export interface ICenteredViewStyles extends ISplitViewStyles { export class CenteredViewLayout { - private splitView: SplitView; + private splitView?: SplitView; private width: number = 0; private height: number = 0; private style: ICenteredViewStyles; private didLayout = false; - private emptyViews: ISplitViewView[]; + private emptyViews: ISplitViewView[] | undefined; private splitViewDisposables: IDisposable[] = []; constructor(private container: HTMLElement, private view: IView, public readonly state: CenteredViewState = GOLDEN_RATIO) { @@ -84,6 +84,9 @@ export class CenteredViewLayout { } private resizeMargins(): void { + if (!this.splitView) { + return; + } this.splitView.resizeView(0, this.state.leftMarginRatio * this.width); this.splitView.resizeView(2, this.state.rightMarginRatio * this.width); } @@ -94,7 +97,7 @@ export class CenteredViewLayout { styles(style: ICenteredViewStyles): void { this.style = style; - if (this.splitView) { + if (this.splitView && this.emptyViews) { this.splitView.style(this.style); this.emptyViews[0].element.style.backgroundColor = this.style.background.toString(); this.emptyViews[1].element.style.backgroundColor = this.style.background.toString(); @@ -115,8 +118,10 @@ export class CenteredViewLayout { }); this.splitViewDisposables.push(this.splitView.onDidSashChange(() => { - this.state.leftMarginRatio = this.splitView.getViewSize(0) / this.width; - this.state.rightMarginRatio = this.splitView.getViewSize(2) / this.width; + if (this.splitView) { + this.state.leftMarginRatio = this.splitView.getViewSize(0) / this.width; + this.state.rightMarginRatio = this.splitView.getViewSize(2) / this.width; + } })); this.splitViewDisposables.push(this.splitView.onDidSashReset(() => { this.state.leftMarginRatio = GOLDEN_RATIO.leftMarginRatio; @@ -130,9 +135,13 @@ export class CenteredViewLayout { this.splitView.addView(this.emptyViews[0], this.state.leftMarginRatio * this.width, 0); this.splitView.addView(this.emptyViews[1], this.state.rightMarginRatio * this.width, 2); } else { - this.container.removeChild(this.splitView.el); + if (this.splitView) { + this.container.removeChild(this.splitView.el); + } this.splitViewDisposables = dispose(this.splitViewDisposables); - this.splitView.dispose(); + if (this.splitView) { + this.splitView.dispose(); + } this.splitView = undefined; this.emptyViews = undefined; this.container.appendChild(this.view.element); diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 531575bb123..2892dde50a9 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -560,7 +560,7 @@ export class GridView implements IDisposable { get maximumWidth(): number { return this.root.maximumHeight; } get maximumHeight(): number { return this.root.maximumHeight; } - private _onDidChange = new Relay<{ width: number; height: number; }>(); + private _onDidChange = new Relay<{ width: number; height: number; } | undefined>(); readonly onDidChange = this._onDidChange.event; constructor(options: IGridViewOptions = {}) { diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index 58e154c751e..c6f06f0f9c0 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -90,8 +90,8 @@ export class HighlightedLabel implements IDisposable { } dispose() { - this.text = null; - this.highlights = null; + this.text = null!; // StrictNullOverride: nulling out ok in dispose + this.highlights = null!; // StrictNullOverride: nulling out ok in dispose } static escapeNewLines(text: string, highlights: IHighlight[]): string { diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 5754251a97d..48114650609 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -751,7 +751,7 @@ function getContiguousRangeContaining(range: number[], value: number): number[] return []; } - const result = []; + const result: number[] = []; let i = index - 1; while (i >= 0 && range[i] === value - (index - i)) { result.push(range[i--]); @@ -771,7 +771,7 @@ function getContiguousRangeContaining(range: number[], value: number): number[] * betweem them (OR). */ function disjunction(one: number[], other: number[]): number[] { - const result = []; + const result: number[] = []; let i = 0, j = 0; while (i < one.length || j < other.length) { @@ -799,7 +799,7 @@ function disjunction(one: number[], other: number[]): number[] { * complement between them (XOR). */ function relativeComplement(one: number[], other: number[]): number[] { - const result = []; + const result: number[] = []; let i = 0, j = 0; while (i < one.length || j < other.length) { diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index 3c4d78563b9..d70ddf174ea 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -188,6 +188,6 @@ export class RangeMap { } dispose() { - this.groups = null; + this.groups = null!; // StrictNullOverride: nulling out ok in dispose } } \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/rowCache.ts b/src/vs/base/browser/ui/list/rowCache.ts index 756b1f9a678..b7007c2fc0f 100644 --- a/src/vs/base/browser/ui/list/rowCache.ts +++ b/src/vs/base/browser/ui/list/rowCache.ts @@ -8,14 +8,16 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { $, removeClass } from 'vs/base/browser/dom'; export interface IRow { - domNode: HTMLElement; + domNode: HTMLElement | null; templateId: string; templateData: any; } function removeFromParent(element: HTMLElement): void { try { - element.parentElement.removeChild(element); + if (element.parentElement) { + element.parentElement.removeChild(element); + } } catch (e) { // this will throw if this happens due to a blur event, nasty business } @@ -57,8 +59,10 @@ export class RowCache implements IDisposable { private releaseRow(row: IRow): void { const { domNode, templateId } = row; - removeClass(domNode, 'scrolling'); - removeFromParent(domNode); + if (domNode) { + removeClass(domNode, 'scrolling'); + removeFromParent(domNode); + } const cache = this.getTemplateCache(templateId); cache.push(row); @@ -95,6 +99,6 @@ export class RowCache implements IDisposable { dispose(): void { this.garbageCollect(); this.cache.clear(); - this.renderers = null; + this.renderers = null!; // StrictNullOverride: nulling out ok in dispose } } \ No newline at end of file diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 65a883bcb17..36b40a6995c 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -129,6 +129,13 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { - this.selectElement.title = e.target.value; this.selected = e.target.selectedIndex; this._onDidSelect.fire({ index: e.target.selectedIndex, @@ -301,7 +307,6 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate document.body.removeChild(dragImage), 0); @@ -371,7 +371,7 @@ interface IPanelItem { export class PanelView extends Disposable { - private dnd: IPanelDndController | null; + private dnd: IPanelDndController | undefined; private dndContext: IDndContext = { draggable: null }; private el: HTMLElement; private panelItems: IPanelItem[] = []; diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 47f1c37607a..b55256c330f 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -406,7 +406,7 @@ export class IndexTreeModel implements ITreeModel): number[] { - const location = []; + const location: number[] = []; while (node.parent) { location.push(node.parent.children.indexOf(node)); diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 7aaab83a0a8..408750697f0 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -6,7 +6,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { ISplice } from 'vs/base/common/sequence'; -import { TPromise } from 'vs/base/common/winjs.base'; /** * Returns the last element of an array. @@ -261,12 +260,12 @@ export function top(array: T[], compare: (a: T, b: T) => number, n: number): * @param batch The number of elements to examine before yielding to the event loop. * @return The first n elemnts from array when sorted with compare. */ -export function topAsync(array: T[], compare: (a: T, b: T) => number, n: number, batch: number, token?: CancellationToken): TPromise { +export function topAsync(array: T[], compare: (a: T, b: T) => number, n: number, batch: number, token?: CancellationToken): Promise { if (n === 0) { - return TPromise.as([]); + return Promise.resolve([]); } - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { (async () => { const o = array.length; const result = array.slice(0, n).sort(compare); diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index c0685017f82..6efede87596 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -2,35 +2,40 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; export const enum ScanError { - None, - UnexpectedEndOfComment, - UnexpectedEndOfString, - UnexpectedEndOfNumber, - InvalidUnicode, - InvalidEscapeCharacter, - InvalidCharacter + None = 0, + UnexpectedEndOfComment = 1, + UnexpectedEndOfString = 2, + UnexpectedEndOfNumber = 3, + InvalidUnicode = 4, + InvalidEscapeCharacter = 5, + InvalidCharacter = 6 } export const enum SyntaxKind { - Unknown = 0, - OpenBraceToken, - CloseBraceToken, - OpenBracketToken, - CloseBracketToken, - CommaToken, - ColonToken, - NullKeyword, - TrueKeyword, - FalseKeyword, - StringLiteral, - NumericLiteral, - LineCommentTrivia, - BlockCommentTrivia, - LineBreakTrivia, - Trivia, - EOF + OpenBraceToken = 1, + CloseBraceToken = 2, + OpenBracketToken = 3, + CloseBracketToken = 4, + CommaToken = 5, + ColonToken = 6, + NullKeyword = 7, + TrueKeyword = 8, + FalseKeyword = 9, + StringLiteral = 10, + NumericLiteral = 11, + LineCommentTrivia = 12, + BlockCommentTrivia = 13, + LineBreakTrivia = 14, + Trivia = 15, + Unknown = 16, + EOF = 17 } /** @@ -42,7 +47,7 @@ export interface JSONScanner { */ setPosition(pos: number): void; /** - * Read the next token. Returns the tolen code. + * Read the next token. Returns the token code. */ scan(): SyntaxKind; /** @@ -70,6 +75,122 @@ export interface JSONScanner { */ getTokenError(): ScanError; } + + +export interface ParseError { + error: ParseErrorCode; + offset: number; + length: number; +} + +export const enum ParseErrorCode { + InvalidSymbol = 1, + InvalidNumberFormat = 2, + PropertyNameExpected = 3, + ValueExpected = 4, + ColonExpected = 5, + CommaExpected = 6, + CloseBraceExpected = 7, + CloseBracketExpected = 8, + EndOfFileExpected = 9, + InvalidCommentToken = 10, + UnexpectedEndOfComment = 11, + UnexpectedEndOfString = 12, + UnexpectedEndOfNumber = 13, + InvalidUnicode = 14, + InvalidEscapeCharacter = 15, + InvalidCharacter = 16 +} + +export type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null'; + +export interface Node { + readonly type: NodeType; + readonly value?: any; + readonly offset: number; + readonly length: number; + readonly colonOffset?: number; + readonly parent?: Node; + readonly children?: Node[]; +} + +export type Segment = string | number; +export type JSONPath = Segment[]; + +export interface Location { + /** + * The previous property key or literal value (string, number, boolean or null) or undefined. + */ + previousNode?: Node; + /** + * The path describing the location in the JSON document. The path consists of a sequence strings + * representing an object property or numbers for array indices. + */ + path: JSONPath; + /** + * Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices). + * '*' will match a single segment, of any property name or index. + * '**' will match a sequece of segments or no segment, of any property name or index. + */ + matches: (patterns: JSONPath) => boolean; + /** + * If set, the location's offset is at a property key. + */ + isAtPropertyKey: boolean; +} + +export interface ParseOptions { + disallowComments?: boolean; + allowTrailingComma?: boolean; +} + +export interface JSONVisitor { + /** + * Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace. + */ + onObjectBegin?: (offset: number, length: number) => void; + + /** + * Invoked when a property is encountered. The offset and length represent the location of the property name. + */ + onObjectProperty?: (property: string, offset: number, length: number) => void; + + /** + * Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace. + */ + onObjectEnd?: (offset: number, length: number) => void; + + /** + * Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket. + */ + onArrayBegin?: (offset: number, length: number) => void; + + /** + * Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket. + */ + onArrayEnd?: (offset: number, length: number) => void; + + /** + * Invoked when a literal value is encountered. The offset and length represent the location of the literal value. + */ + onLiteralValue?: (value: any, offset: number, length: number) => void; + + /** + * Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator. + */ + onSeparator?: (character: string, offset: number, length: number) => void; + + /** + * When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment. + */ + onComment?: (offset: number, length: number) => void; + + /** + * Invoked on an error. + */ + onError?: (error: ParseErrorCode, offset: number, length: number) => void; +} + /** * Creates a JSON scanner on the given text. * If ignoreTrivia is set, whitespaces or comments are ignored. @@ -584,53 +705,136 @@ const enum CharacterCodes { verticalTab = 0x0B, // \v } - - -export interface ParseError { - error: ParseErrorCode; -} - -export const enum ParseErrorCode { - InvalidSymbol, - InvalidNumberFormat, - PropertyNameExpected, - ValueExpected, - ColonExpected, - CommaExpected, - CloseBraceExpected, - CloseBracketExpected, - EndOfFileExpected -} - -export type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null'; - -function getLiteralNodeType(value: any): NodeType { - switch (typeof value) { - case 'boolean': return 'boolean'; - case 'number': return 'number'; - case 'string': return 'string'; - default: return 'null'; - } -} - -export interface Node { +interface NodeImpl extends Node { type: NodeType; value?: any; offset: number; length: number; - columnOffset?: number; - parent?: Node; - children?: Node[]; + colonOffset?: number; + parent?: NodeImpl; + children?: NodeImpl[]; } -export type Segment = string | number; -export type JSONPath = Segment[]; +/** + * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index. + */ +export function getLocation(text: string, position: number): Location { + let segments: Segment[] = []; // strings or numbers + let earlyReturnException = new Object(); + let previousNode: NodeImpl | undefined = void 0; + const previousNodeInst: NodeImpl = { + value: {}, + offset: 0, + length: 0, + type: 'object', + parent: void 0 + }; + let isAtPropertyKey = false; + function setPreviousNode(value: string, offset: number, length: number, type: NodeType) { + previousNodeInst.value = value; + previousNodeInst.offset = offset; + previousNodeInst.length = length; + previousNodeInst.type = type; + previousNodeInst.colonOffset = void 0; + previousNode = previousNodeInst; + } + try { -export interface ParseOptions { - disallowComments?: boolean; - disallowTrailingComma?: boolean; + visit(text, { + onObjectBegin: (offset: number, length: number) => { + if (position <= offset) { + throw earlyReturnException; + } + previousNode = void 0; + isAtPropertyKey = position > offset; + segments.push(''); // push a placeholder (will be replaced) + }, + onObjectProperty: (name: string, offset: number, length: number) => { + if (position < offset) { + throw earlyReturnException; + } + setPreviousNode(name, offset, length, 'property'); + segments[segments.length - 1] = name; + if (position <= offset + length) { + throw earlyReturnException; + } + }, + onObjectEnd: (offset: number, length: number) => { + if (position <= offset) { + throw earlyReturnException; + } + previousNode = void 0; + segments.pop(); + }, + onArrayBegin: (offset: number, length: number) => { + if (position <= offset) { + throw earlyReturnException; + } + previousNode = void 0; + segments.push(0); + }, + onArrayEnd: (offset: number, length: number) => { + if (position <= offset) { + throw earlyReturnException; + } + previousNode = void 0; + segments.pop(); + }, + onLiteralValue: (value: any, offset: number, length: number) => { + if (position < offset) { + throw earlyReturnException; + } + setPreviousNode(value, offset, length, getLiteralNodeType(value)); + + if (position <= offset + length) { + throw earlyReturnException; + } + }, + onSeparator: (sep: string, offset: number, length: number) => { + if (position <= offset) { + throw earlyReturnException; + } + if (sep === ':' && previousNode && previousNode.type === 'property') { + previousNode.colonOffset = offset; + isAtPropertyKey = false; + previousNode = void 0; + } else if (sep === ',') { + let last = segments[segments.length - 1]; + if (typeof last === 'number') { + segments[segments.length - 1] = last + 1; + } else { + isAtPropertyKey = true; + segments[segments.length - 1] = ''; + } + previousNode = void 0; + } + } + }); + } catch (e) { + if (e !== earlyReturnException) { + throw e; + } + } + + return { + path: segments, + previousNode, + isAtPropertyKey, + matches: (pattern: Segment[]) => { + let k = 0; + for (let i = 0; k < pattern.length && i < segments.length; i++) { + if (pattern[k] === segments[i] || pattern[k] === '*') { + k++; + } else if (pattern[k] !== '**') { + return false; + } + } + return k === pattern.length; + } + }; } + /** * Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. * Therefore always check the errors list to find out if the input was valid. @@ -673,8 +877,8 @@ export function parse(text: string, errors: ParseError[] = [], options?: ParseOp currentParent = previousParents.pop(); }, onLiteralValue: onValue, - onError: (error: ParseErrorCode) => { - errors.push({ error: error }); + onError: (error: ParseErrorCode, offset: number, length: number) => { + errors.push({ error, offset, length }); } }; visit(text, visitor, options); @@ -686,22 +890,17 @@ export function parse(text: string, errors: ParseError[] = [], options?: ParseOp * Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. */ export function parseTree(text: string, errors: ParseError[] = [], options?: ParseOptions): Node { - let currentParent: Node | undefined = { type: 'array', offset: -1, length: -1, children: [] }; // artificial root + let currentParent: NodeImpl = { type: 'array', offset: -1, length: -1, children: [], parent: void 0 }; // artificial root function ensurePropertyComplete(endOffset: number) { - if (currentParent && currentParent.type === 'property') { + if (currentParent.type === 'property') { currentParent.length = endOffset - currentParent.offset; - currentParent = currentParent.parent; + currentParent = currentParent.parent!; } } function onValue(valueNode: Node): Node { - if (currentParent) { - if (!currentParent.children) { - currentParent.children = []; - } - currentParent.children.push(valueNode); - } + currentParent.children!.push(valueNode); return valueNode; } @@ -714,22 +913,16 @@ export function parseTree(text: string, errors: ParseError[] = [], options?: Par currentParent.children!.push({ type: 'string', value: name, offset, length, parent: currentParent }); }, onObjectEnd: (offset: number, length: number) => { - if (!currentParent) { - throw new Error('No current parent node'); - } currentParent.length = offset + length - currentParent.offset; - currentParent = currentParent.parent; + currentParent = currentParent.parent!; ensurePropertyComplete(offset + length); }, onArrayBegin: (offset: number, length: number) => { currentParent = onValue({ type: 'array', offset, length: -1, parent: currentParent, children: [] }); }, onArrayEnd: (offset: number, length: number) => { - if (!currentParent) { - throw new Error('No current parent node'); - } currentParent.length = offset + length - currentParent.offset; - currentParent = currentParent.parent; + currentParent = currentParent.parent!; ensurePropertyComplete(offset + length); }, onLiteralValue: (value: any, offset: number, length: number) => { @@ -737,27 +930,30 @@ export function parseTree(text: string, errors: ParseError[] = [], options?: Par ensurePropertyComplete(offset + length); }, onSeparator: (sep: string, offset: number, length: number) => { - if (currentParent && currentParent.type === 'property') { + if (currentParent.type === 'property') { if (sep === ':') { - currentParent.columnOffset = offset; + currentParent.colonOffset = offset; } else if (sep === ',') { ensurePropertyComplete(offset); } } }, - onError: (error: ParseErrorCode) => { - errors.push({ error: error }); + onError: (error: ParseErrorCode, offset: number, length: number) => { + errors.push({ error, offset, length }); } }; visit(text, visitor, options); - let result = currentParent!.children![0]; + let result = currentParent.children![0]; if (result) { delete result.parent; } return result; } +/** + * Finds the node at the given path in a JSON DOM. + */ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined { if (!root) { return void 0; @@ -765,12 +961,12 @@ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined let node = root; for (let segment of path) { if (typeof segment === 'string') { - if (node.type !== 'object') { + if (node.type !== 'object' || !Array.isArray(node.children)) { return void 0; } let found = false; - for (const propertyNode of node.children || []) { - if (propertyNode.children && propertyNode.children[0].value === segment) { + for (const propertyNode of node.children) { + if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment) { node = propertyNode.children[1]; found = true; break; @@ -781,26 +977,84 @@ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined } } else { let index = segment; - if (node.type !== 'array' || index < 0 || index >= node.children!.length) { + if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) { return void 0; } - node = node.children![index]; + node = node.children[index]; } } return node; } -export function getNodeValue(node: Node): any { - if (node.type === 'array') { - return node.children ? node.children.map(getNodeValue) : []; - } else if (node.type === 'object') { - let obj = {}; - for (let prop of node.children || []) { - obj[prop.children![0].value] = getNodeValue(prop.children![1]); - } - return obj; +/** + * Gets the JSON path of the given JSON DOM node + */ +export function getNodePath(node: Node): JSONPath { + if (!node.parent || !node.parent.children) { + return []; } - return node.value; + let path = getNodePath(node.parent); + if (node.parent.type === 'property') { + let key = node.parent.children[0].value; + path.push(key); + } else if (node.parent.type === 'array') { + let index = node.parent.children.indexOf(node); + if (index !== -1) { + path.push(index); + } + } + return path; +} + +/** + * Evaluates the JavaScript object of the given JSON DOM node + */ +export function getNodeValue(node: Node): any { + switch (node.type) { + case 'array': + return node.children!.map(getNodeValue); + case 'object': + let obj = Object.create(null); + for (let prop of node.children!) { + let valueNode = prop.children![1]; + if (valueNode) { + obj[prop.children![0].value] = getNodeValue(valueNode); + } + } + return obj; + case 'null': + case 'string': + case 'number': + case 'boolean': + return node.value; + default: + return void 0; + } + +} + +export function contains(node: Node, offset: number, includeRightBound = false): boolean { + return (offset >= node.offset && offset < (node.offset + node.length)) || includeRightBound && (offset === (node.offset + node.length)); +} + +/** + * Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset. + */ +export function findNodeAtOffset(node: Node, offset: number, includeRightBound = false): Node | undefined { + if (contains(node, offset, includeRightBound)) { + let children = node.children; + if (Array.isArray(children)) { + for (let i = 0; i < children.length && children[i].offset <= offset; i++) { + let item = findNodeAtOffset(children[i], offset, includeRightBound); + if (item) { + return item; + } + } + + } + return node; + } + return void 0; } @@ -811,10 +1065,10 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions let _scanner = createScanner(text, false); - function toNoArgVisit(visitFunction: ((offset: number, length: number) => void) | undefined): () => void { + function toNoArgVisit(visitFunction?: (offset: number, length: number) => void): () => void { return visitFunction ? () => visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true; } - function toOneArgVisit(visitFunction: ((arg: T, offset: number, length: number) => void) | undefined): (arg: T) => void { + function toOneArgVisit(visitFunction?: (arg: T, offset: number, length: number) => void): (arg: T) => void { return visitFunction ? (arg: T) => visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true; } @@ -825,18 +1079,43 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions onArrayEnd = toNoArgVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisit(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), + onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError); let disallowComments = options && options.disallowComments; - let disallowTrailingComma = options && options.disallowTrailingComma; + let allowTrailingComma = options && options.allowTrailingComma; function scanNext(): SyntaxKind { while (true) { let token = _scanner.scan(); + switch (_scanner.getTokenError()) { + case ScanError.InvalidUnicode: + handleError(ParseErrorCode.InvalidUnicode); + break; + case ScanError.InvalidEscapeCharacter: + handleError(ParseErrorCode.InvalidEscapeCharacter); + break; + case ScanError.UnexpectedEndOfNumber: + handleError(ParseErrorCode.UnexpectedEndOfNumber); + break; + case ScanError.UnexpectedEndOfComment: + if (!disallowComments) { + handleError(ParseErrorCode.UnexpectedEndOfComment); + } + break; + case ScanError.UnexpectedEndOfString: + handleError(ParseErrorCode.UnexpectedEndOfString); + break; + case ScanError.InvalidCharacter: + handleError(ParseErrorCode.InvalidCharacter); + break; + } switch (token) { case SyntaxKind.LineCommentTrivia: case SyntaxKind.BlockCommentTrivia: if (disallowComments) { - handleError(ParseErrorCode.InvalidSymbol); + handleError(ParseErrorCode.InvalidCommentToken); + } else { + onComment(); } break; case SyntaxKind.Unknown: @@ -940,7 +1219,7 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions } onSeparator(','); scanNext(); // consume comma - if (_scanner.getToken() === SyntaxKind.CloseBraceToken && !disallowTrailingComma) { + if (_scanner.getToken() === SyntaxKind.CloseBraceToken && allowTrailingComma) { break; } } else if (needsComma) { @@ -972,7 +1251,7 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions } onSeparator(','); scanNext(); // consume comma - if (_scanner.getToken() === SyntaxKind.CloseBracketToken && !disallowTrailingComma) { + if (_scanner.getToken() === SyntaxKind.CloseBracketToken && allowTrailingComma) { break; } } else if (needsComma) { @@ -1019,44 +1298,45 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions return true; } -export interface JSONVisitor { - /** - * Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace. - */ - onObjectBegin?: (offset: number, length: number) => void; +/** + * Takes JSON with JavaScript-style comments and remove + * them. Optionally replaces every none-newline character + * of comments with a replaceCharacter + */ +export function stripComments(text: string, replaceCh?: string): string { - /** - * Invoked when a property is encountered. The offset and length represent the location of the property name. - */ - onObjectProperty?: (property: string, offset: number, length: number) => void; + let _scanner = createScanner(text), + parts: string[] = [], + kind: SyntaxKind, + offset = 0, + pos: number; - /** - * Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace. - */ - onObjectEnd?: (offset: number, length: number) => void; + do { + pos = _scanner.getPosition(); + kind = _scanner.scan(); + switch (kind) { + case SyntaxKind.LineCommentTrivia: + case SyntaxKind.BlockCommentTrivia: + case SyntaxKind.EOF: + if (offset !== pos) { + parts.push(text.substring(offset, pos)); + } + if (replaceCh !== void 0) { + parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh)); + } + offset = _scanner.getPosition(); + break; + } + } while (kind !== SyntaxKind.EOF); - /** - * Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket. - */ - onArrayBegin?: (offset: number, length: number) => void; - - /** - * Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket. - */ - onArrayEnd?: (offset: number, length: number) => void; - - /** - * Invoked when a literal value is encountered. The offset and length represent the location of the literal value. - */ - onLiteralValue?: (value: any, offset: number, length: number) => void; - - /** - * Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator. - */ - onSeparator?: (charcter: string, offset: number, length: number) => void; - - /** - * Invoked on an error. - */ - onError?: (error: ParseErrorCode, offset: number, length: number) => void; + return parts.join(''); } + +function getLiteralNodeType(value: any): NodeType { + switch (typeof value) { + case 'boolean': return 'boolean'; + case 'number': return 'number'; + case 'string': return 'string'; + default: return 'null'; + } +} \ No newline at end of file diff --git a/src/vs/base/common/jsonEdit.ts b/src/vs/base/common/jsonEdit.ts index 6c45671e337..33ab2fdca80 100644 --- a/src/vs/base/common/jsonEdit.ts +++ b/src/vs/base/common/jsonEdit.ts @@ -2,15 +2,18 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { ParseError, Node, JSONPath, Segment, parseTree, findNodeAtLocation } from './json'; +import { Edit, format, isEOL, FormattingOptions } from './jsonFormatter'; -import { ParseError, Node, parseTree, findNodeAtLocation, JSONPath, Segment } from 'vs/base/common/json'; -import { Edit, FormattingOptions, format, applyEdit } from 'vs/base/common/jsonFormatter'; export function removeProperty(text: string, path: JSONPath, formattingOptions: FormattingOptions): Edit[] { return setProperty(text, path, void 0, formattingOptions); } -export function setProperty(text: string, path: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number): Edit[] { +export function setProperty(text: string, originalPath: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number): Edit[] { + let path = originalPath.slice(); let errors: ParseError[] = []; let root = parseTree(text, errors); let parent: Node | undefined = void 0; @@ -36,20 +39,23 @@ export function setProperty(text: string, path: JSONPath, value: any, formatting throw new Error('Can not delete in empty document'); } return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, formattingOptions); - } else if (parent.type === 'object' && typeof lastSegment === 'string') { + } else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) { let existing = findNodeAtLocation(parent, [lastSegment]); if (existing !== void 0) { if (value === void 0) { // delete - let propertyIndex = parent.children && existing.parent ? parent.children.indexOf(existing.parent) : -1; + if (!existing.parent) { + throw new Error('Malformed AST'); + } + let propertyIndex = parent.children.indexOf(existing.parent); let removeBegin: number; - let removeEnd = existing.parent!.offset + existing.parent!.length; + let removeEnd = existing.parent.offset + existing.parent.length; if (propertyIndex > 0) { // remove the comma of the previous node - let previous = parent.children![propertyIndex - 1]; + let previous = parent.children[propertyIndex - 1]; removeBegin = previous.offset + previous.length; } else { removeBegin = parent.offset + 1; - if (parent.children && parent.children.length > 1) { + if (parent.children.length > 1) { // remove the comma of the next node let next = parent.children[1]; removeEnd = next.offset; @@ -65,25 +71,25 @@ export function setProperty(text: string, path: JSONPath, value: any, formatting return []; // property does not exist, nothing to do } let newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`; - let index = getInsertionIndex ? getInsertionIndex(parent.children!.map(p => p.children![0].value)) : parent.children!.length; + let index = getInsertionIndex ? getInsertionIndex(parent.children.map(p => p.children![0].value)) : parent.children.length; let edit: Edit; if (index > 0) { - let previous = parent.children![index - 1]; + let previous = parent.children[index - 1]; edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty }; - } else if (parent.children!.length === 0) { + } else if (parent.children.length === 0) { edit = { offset: parent.offset + 1, length: 0, content: newProperty }; } else { edit = { offset: parent.offset + 1, length: 0, content: newProperty + ',' }; } return withFormatting(text, edit, formattingOptions); } - } else if (parent.type === 'array' && typeof lastSegment === 'number') { + } else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) { let insertIndex = lastSegment; if (insertIndex === -1) { // Insert let newProperty = `${JSON.stringify(value)}`; let edit: Edit; - if (!parent.children || parent.children.length === 0) { + if (parent.children.length === 0) { edit = { offset: parent.offset + 1, length: 0, content: newProperty }; } else { let previous = parent.children[parent.children.length - 1]; @@ -91,7 +97,7 @@ export function setProperty(text: string, path: JSONPath, value: any, formatting } return withFormatting(text, edit, formattingOptions); } else { - if (value === void 0 && parent.children && parent.children.length >= 0) { + if (value === void 0 && parent.children.length >= 0) { //Removal let removalIndex = lastSegment; let toRemove = parent.children[removalIndex]; @@ -125,6 +131,15 @@ function withFormatting(text: string, edit: Edit, formattingOptions: FormattingO // format the new text let begin = edit.offset; let end = edit.offset + edit.content.length; + if (edit.length === 0 || edit.content.length === 0) { // insert or remove + while (begin > 0 && !isEOL(newText, begin - 1)) { + begin--; + } + while (end < newText.length && !isEOL(newText, end)) { + end++; + } + } + let edits = format(newText, { offset: begin, length: end - begin }, formattingOptions); // apply the formatting edits and track the begin and end offsets of the changes @@ -138,4 +153,12 @@ function withFormatting(text: string, edit: Edit, formattingOptions: FormattingO // create a single edit with all changes let editLength = text.length - (newText.length - end) - begin; return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }]; +} + +export function applyEdit(text: string, edit: Edit): string { + return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length); +} + +export function isWS(text: string, offset: number) { + return '\r\n \t'.indexOf(text.charAt(offset)) !== -1; } \ No newline at end of file diff --git a/src/vs/base/common/jsonFormatter.ts b/src/vs/base/common/jsonFormatter.ts index e7b8fe6d71f..a2306b5cb28 100644 --- a/src/vs/base/common/jsonFormatter.ts +++ b/src/vs/base/common/jsonFormatter.ts @@ -2,64 +2,84 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +'use strict'; -import * as Json from './json'; +import { createScanner, SyntaxKind, ScanError } from './json'; export interface FormattingOptions { /** * If indentation is based on spaces (`insertSpaces` = true), then what is the number of spaces that make an indent? */ - tabSize: number; + tabSize?: number; /** * Is indentation based on spaces? */ - insertSpaces: boolean; + insertSpaces?: boolean; /** - * The default end of line line character + * The default 'end of line' character. If not set, '\n' is used as default. */ - eol: string; + eol?: string; } +/** + * Represents a text modification + */ export interface Edit { + /** + * The start offset of the modification. + */ offset: number; + /** + * The length of the modification. Must not be negative. Empty length represents an *insert*. + */ length: number; + /** + * The new content. Empty content represents a *remove*. + */ content: string; } -export function applyEdit(text: string, edit: Edit): string { - return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length); +/** + * A text range in the document +*/ +export interface Range { + /** + * The start offset of the range. + */ + offset: number; + /** + * The length of the range. Must not be negative. + */ + length: number; } -export function applyEdits(text: string, edits: Edit[]): string { - for (let i = edits.length - 1; i >= 0; i--) { - text = applyEdit(text, edits[i]); - } - return text; -} -export function format(documentText: string, range: { offset: number, length: number }, options: FormattingOptions): Edit[] { +export function format(documentText: string, range: Range | undefined, options: FormattingOptions): Edit[] { let initialIndentLevel: number; - let value: string; + let formatText: string; + let formatTextStart: number; let rangeStart: number; let rangeEnd: number; if (range) { rangeStart = range.offset; rangeEnd = rangeStart + range.length; - while (rangeStart > 0 && !isEOL(documentText, rangeStart - 1)) { - rangeStart--; - } - let scanner = Json.createScanner(documentText, true); - scanner.setPosition(rangeEnd); - scanner.scan(); - rangeEnd = scanner.getPosition(); - value = documentText.substring(rangeStart, rangeEnd); - initialIndentLevel = computeIndentLevel(value, 0, options); + formatTextStart = rangeStart; + while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) { + formatTextStart--; + } + let endOffset = rangeEnd; + while (endOffset < documentText.length && !isEOL(documentText, endOffset)) { + endOffset++; + } + formatText = documentText.substring(formatTextStart, endOffset); + initialIndentLevel = computeIndentLevel(formatText, options); } else { - value = documentText; + formatText = documentText; + initialIndentLevel = 0; + formatTextStart = 0; rangeStart = 0; rangeEnd = documentText.length; - initialIndentLevel = 0; } let eol = getEOL(options, documentText); @@ -67,75 +87,78 @@ export function format(documentText: string, range: { offset: number, length: nu let indentLevel = 0; let indentValue: string; if (options.insertSpaces) { - indentValue = repeat(' ', options.tabSize); + indentValue = repeat(' ', options.tabSize || 4); } else { indentValue = '\t'; } - let scanner = Json.createScanner(value, false); + let scanner = createScanner(formatText, false); + let hasError = false; function newLineAndIndent(): string { return eol + repeat(indentValue, initialIndentLevel + indentLevel); } - function scanNext(): Json.SyntaxKind { + function scanNext(): SyntaxKind { let token = scanner.scan(); lineBreak = false; - while (token === Json.SyntaxKind.Trivia || token === Json.SyntaxKind.LineBreakTrivia) { - lineBreak = lineBreak || (token === Json.SyntaxKind.LineBreakTrivia); + while (token === SyntaxKind.Trivia || token === SyntaxKind.LineBreakTrivia) { + lineBreak = lineBreak || (token === SyntaxKind.LineBreakTrivia); token = scanner.scan(); } + hasError = token === SyntaxKind.Unknown || scanner.getTokenError() !== ScanError.None; return token; } let editOperations: Edit[] = []; function addEdit(text: string, startOffset: number, endOffset: number) { - if (documentText.substring(startOffset, endOffset) !== text) { + if (!hasError && startOffset < rangeEnd && endOffset > rangeStart && documentText.substring(startOffset, endOffset) !== text) { editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text }); } } let firstToken = scanNext(); - if (firstToken !== Json.SyntaxKind.EOF) { - let firstTokenStart = scanner.getTokenOffset() + rangeStart; + + if (firstToken !== SyntaxKind.EOF) { + let firstTokenStart = scanner.getTokenOffset() + formatTextStart; let initialIndent = repeat(indentValue, initialIndentLevel); - addEdit(initialIndent, rangeStart, firstTokenStart); + addEdit(initialIndent, formatTextStart, firstTokenStart); } - while (firstToken !== Json.SyntaxKind.EOF) { - let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + rangeStart; + while (firstToken !== SyntaxKind.EOF) { + let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart; let secondToken = scanNext(); let replaceContent = ''; - while (!lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) { + while (!lineBreak && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) { // comments on the same line: keep them on the same line, but ignore them otherwise - let commentTokenStart = scanner.getTokenOffset() + rangeStart; + let commentTokenStart = scanner.getTokenOffset() + formatTextStart; addEdit(' ', firstTokenEnd, commentTokenStart); - firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + rangeStart; - replaceContent = secondToken === Json.SyntaxKind.LineCommentTrivia ? newLineAndIndent() : ''; + firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart; + replaceContent = secondToken === SyntaxKind.LineCommentTrivia ? newLineAndIndent() : ''; secondToken = scanNext(); } - if (secondToken === Json.SyntaxKind.CloseBraceToken) { - if (firstToken !== Json.SyntaxKind.OpenBraceToken) { + if (secondToken === SyntaxKind.CloseBraceToken) { + if (firstToken !== SyntaxKind.OpenBraceToken) { indentLevel--; replaceContent = newLineAndIndent(); } - } else if (secondToken === Json.SyntaxKind.CloseBracketToken) { - if (firstToken !== Json.SyntaxKind.OpenBracketToken) { + } else if (secondToken === SyntaxKind.CloseBracketToken) { + if (firstToken !== SyntaxKind.OpenBracketToken) { indentLevel--; replaceContent = newLineAndIndent(); } - } else if (secondToken !== Json.SyntaxKind.EOF) { + } else { switch (firstToken) { - case Json.SyntaxKind.OpenBracketToken: - case Json.SyntaxKind.OpenBraceToken: + case SyntaxKind.OpenBracketToken: + case SyntaxKind.OpenBraceToken: indentLevel++; replaceContent = newLineAndIndent(); break; - case Json.SyntaxKind.CommaToken: - case Json.SyntaxKind.LineCommentTrivia: + case SyntaxKind.CommaToken: + case SyntaxKind.LineCommentTrivia: replaceContent = newLineAndIndent(); break; - case Json.SyntaxKind.BlockCommentTrivia: + case SyntaxKind.BlockCommentTrivia: if (lineBreak) { replaceContent = newLineAndIndent(); } else { @@ -143,24 +166,37 @@ export function format(documentText: string, range: { offset: number, length: nu replaceContent = ' '; } break; - case Json.SyntaxKind.ColonToken: + case SyntaxKind.ColonToken: replaceContent = ' '; break; - case Json.SyntaxKind.NullKeyword: - case Json.SyntaxKind.TrueKeyword: - case Json.SyntaxKind.FalseKeyword: - case Json.SyntaxKind.NumericLiteral: - if (secondToken === Json.SyntaxKind.NullKeyword || secondToken === Json.SyntaxKind.FalseKeyword || secondToken === Json.SyntaxKind.NumericLiteral) { + case SyntaxKind.StringLiteral: + if (secondToken === SyntaxKind.ColonToken) { + replaceContent = ''; + break; + } + // fall through + case SyntaxKind.NullKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NumericLiteral: + case SyntaxKind.CloseBraceToken: + case SyntaxKind.CloseBracketToken: + if (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia) { replaceContent = ' '; + } else if (secondToken !== SyntaxKind.CommaToken && secondToken !== SyntaxKind.EOF) { + hasError = true; } break; + case SyntaxKind.Unknown: + hasError = true; + break; } - if (lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) { + if (lineBreak && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) { replaceContent = newLineAndIndent(); } } - let secondTokenStart = scanner.getTokenOffset() + rangeStart; + let secondTokenStart = scanner.getTokenOffset() + formatTextStart; addEdit(replaceContent, firstTokenEnd, secondTokenStart); firstToken = secondToken; } @@ -175,7 +211,7 @@ function repeat(s: string, count: number): string { return result; } -function computeIndentLevel(content: string, offset: number, options: FormattingOptions): number { +function computeIndentLevel(content: string, options: FormattingOptions): number { let i = 0; let nChars = 0; let tabSize = options.tabSize || 4; @@ -208,6 +244,6 @@ function getEOL(options: FormattingOptions, text: string): string { return (options && options.eol) || '\n'; } -function isEOL(text: string, offset: number) { +export function isEOL(text: string, offset: number) { return '\r\n'.indexOf(text.charAt(offset)) !== -1; } \ No newline at end of file diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index b4dc302fe91..69d295d568f 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -336,7 +336,7 @@ export class URI implements UriComponents { // ---- printing/externalize --------------------------- /** - * Creates a string presentation for this URI. It's guardeed that calling + * Creates a string presentation for this URI. It's guaranteed that calling * `URI.parse` with the result of this function creates an URI which is equal * to this URI. * diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index d11f9a8dc45..af7e3cf162a 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -194,7 +194,7 @@ export function listProcesses(rootPid: number): Promise { if (process.platform === 'linux') { // Flatten rootItem to get a list of all VSCode processes let processes = [rootItem]; - const pids = []; + const pids: number[] = []; while (processes.length) { const process = processes.shift(); pids.push(process.pid); diff --git a/src/vs/base/node/stats.ts b/src/vs/base/node/stats.ts index a65797e127a..fbf895ea0a6 100644 --- a/src/vs/base/node/stats.ts +++ b/src/vs/base/node/stats.ts @@ -5,7 +5,7 @@ import { readdir, stat, exists, readFile } from 'fs'; import { join } from 'path'; -import { parse } from 'vs/base/common/json'; +import { parse, ParseError } from 'vs/base/common/json'; export interface WorkspaceStatItem { name: string; @@ -37,7 +37,7 @@ export function collectLaunchConfigs(folder: string): Promise messages.push(msg)); await timeout(0); diff --git a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts index 841974aaade..ace8984e735 100644 --- a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts +++ b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts @@ -61,8 +61,8 @@ export function score(target: string, query: string, queryLower: string, fuzzy: } function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): [number, number[]] { - const scores = []; - const matches = []; + const scores: number[] = []; + const matches: number[] = []; // // Build Scorer Matrix: @@ -121,7 +121,7 @@ function doScore(query: string, queryLower: string, queryLength: number, target: } // Restore Positions (starting from bottom right of matrix) - const positions = []; + const positions: number[] = []; let queryIndex = queryLength - 1; let targetIndex = targetLength - 1; while (queryIndex >= 0 && targetIndex >= 0) { diff --git a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts index 53d2add5df4..07431d166b8 100644 --- a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts @@ -24,14 +24,14 @@ function toArray(list: ITreeNode[]): T[] { suite('IndexTreeModel', function () { test('ctor', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); assert(model); assert.equal(list.length, 0); }); test('insert', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -53,7 +53,7 @@ suite('IndexTreeModel', function () { }); test('deep insert', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -90,7 +90,7 @@ suite('IndexTreeModel', function () { }); test('deep insert collapsed', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -118,7 +118,7 @@ suite('IndexTreeModel', function () { }); test('delete', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -143,7 +143,7 @@ suite('IndexTreeModel', function () { }); test('nested delete', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -177,7 +177,7 @@ suite('IndexTreeModel', function () { }); test('deep delete', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -205,7 +205,7 @@ suite('IndexTreeModel', function () { }); test('hidden delete', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -230,7 +230,7 @@ suite('IndexTreeModel', function () { }); test('collapse', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -261,7 +261,7 @@ suite('IndexTreeModel', function () { }); test('expand', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -301,7 +301,7 @@ suite('IndexTreeModel', function () { }); test('collapse should recursively adjust visible count', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -334,7 +334,7 @@ suite('IndexTreeModel', function () { }); test('simple filter', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const filter = new class implements ITreeFilter { filter(element: number): TreeVisibility { return element % 2 === 0 ? TreeVisibility.Visible : TreeVisibility.Hidden; @@ -368,7 +368,7 @@ suite('IndexTreeModel', function () { }); test('recursive filter on initial model', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const filter = new class implements ITreeFilter { filter(element: number): TreeVisibility { return element === 0 ? TreeVisibility.Recurse : TreeVisibility.Hidden; @@ -390,7 +390,7 @@ suite('IndexTreeModel', function () { }); test('refilter', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; let shouldFilter = false; const filter = new class implements ITreeFilter { filter(element: number): TreeVisibility { @@ -429,7 +429,7 @@ suite('IndexTreeModel', function () { }); test('recursive filter', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; let query = new RegExp(''); const filter = new class implements ITreeFilter { filter(element: string): TreeVisibility { @@ -475,7 +475,7 @@ suite('IndexTreeModel', function () { }); test('recursive filter with collapse', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; let query = new RegExp(''); const filter = new class implements ITreeFilter { filter(element: string): TreeVisibility { @@ -521,7 +521,7 @@ suite('IndexTreeModel', function () { }); test('recursive filter while collapsed', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; let query = new RegExp(''); const filter = new class implements ITreeFilter { filter(element: string): TreeVisibility { @@ -576,7 +576,7 @@ suite('IndexTreeModel', function () { suite('getNodeLocation', function () { test('simple', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -600,7 +600,7 @@ suite('IndexTreeModel', function () { }); test('with filter', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const filter = new class implements ITreeFilter { filter(element: number): TreeVisibility { return element % 2 === 0 ? TreeVisibility.Visible : TreeVisibility.Hidden; diff --git a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts index 4fc0edf0f12..6aac4a6ad68 100644 --- a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts @@ -24,7 +24,7 @@ function toArray(list: ITreeNode[]): T[] { suite('ObjectTreeModel', function () { test('ctor', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new ObjectTreeModel(toSpliceable(list)); assert(model); assert.equal(list.length, 0); @@ -32,7 +32,7 @@ suite('ObjectTreeModel', function () { }); test('flat', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new ObjectTreeModel(toSpliceable(list)); model.setChildren(null, Iterator.fromArray([ @@ -59,7 +59,7 @@ suite('ObjectTreeModel', function () { }); test('nested', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new ObjectTreeModel(toSpliceable(list)); model.setChildren(null, Iterator.fromArray([ @@ -95,7 +95,7 @@ suite('ObjectTreeModel', function () { }); test('setChildren on collapsed node', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new ObjectTreeModel(toSpliceable(list)); model.setChildren(null, Iterator.fromArray([ @@ -116,7 +116,7 @@ suite('ObjectTreeModel', function () { }); test('setChildren on expanded, unrevealed node', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new ObjectTreeModel(toSpliceable(list)); model.setChildren(null, [ diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 834b2545c28..23cb2ec8eca 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -53,7 +53,7 @@ suite('Async', () => { // Cancelling a sync cancelable promise will fire the cancelled token. // Also, every `then` callback runs in another execution frame. test('CancelablePromise execution order (sync)', function () { - const order = []; + const order: string[] = []; const cancellablePromise = async.createCancelablePromise(token => { order.push('in callback'); @@ -75,7 +75,7 @@ suite('Async', () => { // Cancelling an async cancelable promise is just the same as a sync cancellable promise. test('CancelablePromise execution order (async)', function () { - const order = []; + const order: string[] = []; const cancellablePromise = async.createCancelablePromise(token => { order.push('in callback'); diff --git a/src/vs/base/test/common/json.test.ts b/src/vs/base/test/common/json.test.ts index 1ad67bb26f1..07f67d08448 100644 --- a/src/vs/base/test/common/json.test.ts +++ b/src/vs/base/test/common/json.test.ts @@ -23,8 +23,8 @@ function assertScanError(text: string, expectedKind: SyntaxKind, scanError: Scan assert.equal(scanner.getTokenError(), scanError); } -function assertValidParse(input: string, expected: any, options?: ParseOptions): void { - var errors: { error: ParseErrorCode }[] = []; +function assertValidParse(input: string, expected: any, options: ParseOptions = { allowTrailingComma: true }): void { + var errors: ParseError[] = []; var actual = parse(input, errors, options); if (errors.length !== 0) { @@ -33,15 +33,15 @@ function assertValidParse(input: string, expected: any, options?: ParseOptions): assert.deepEqual(actual, expected); } -function assertInvalidParse(input: string, expected: any, options?: ParseOptions): void { - var errors: { error: ParseErrorCode }[] = []; +function assertInvalidParse(input: string, expected: any, options: ParseOptions = { allowTrailingComma: true }): void { + var errors: ParseError[] = []; var actual = parse(input, errors, options); assert(errors.length > 0); assert.deepEqual(actual, expected); } -function assertTree(input: string, expected: any, expectedErrors: number[] = [], options?: ParseOptions): void { +function assertTree(input: string, expected: any, expectedErrors: number[] = [], options: ParseOptions = { allowTrailingComma: true }): void { var errors: ParseError[] = []; var actual = parseTree(input, errors, options); @@ -50,7 +50,7 @@ function assertTree(input: string, expected: any, expectedErrors: number[] = [], if (node.children) { for (let child of node.children) { assert.equal(node, child.parent); - delete child.parent; // delete to avoid recursion in deep equal + delete (child).parent; // delete to avoid recursion in deep equal checkParent(child); } } @@ -201,7 +201,7 @@ suite('JSON', () => { test('parse: objects with errors', () => { assertInvalidParse('{,}', {}); - assertInvalidParse('{ "foo": true, }', { foo: true }, { disallowTrailingComma: true }); + assertInvalidParse('{ "foo": true, }', { foo: true }, { allowTrailingComma: false }); assertInvalidParse('{ "bar": 8 "xoo": "foo" }', { bar: 8, xoo: 'foo' }); assertInvalidParse('{ ,"bar": 8 }', { bar: 8 }); assertInvalidParse('{ ,"bar": 8, "foo" }', { bar: 8 }); @@ -211,10 +211,10 @@ suite('JSON', () => { test('parse: array with errors', () => { assertInvalidParse('[,]', []); - assertInvalidParse('[ 1, 2, ]', [1, 2], { disallowTrailingComma: true }); + assertInvalidParse('[ 1, 2, ]', [1, 2], { allowTrailingComma: false }); assertInvalidParse('[ 1 2, 3 ]', [1, 2, 3]); assertInvalidParse('[ ,1, 2, 3 ]', [1, 2, 3]); - assertInvalidParse('[ ,1, 2, 3, ]', [1, 2, 3], { disallowTrailingComma: true }); + assertInvalidParse('[ ,1, 2, 3, ]', [1, 2, 3], { allowTrailingComma: false }); }); test('parse: disallow commments', () => { @@ -230,14 +230,14 @@ suite('JSON', () => { // default is allow assertValidParse('{ "hello": [], }', { hello: [] }); - let options = { disallowTrailingComma: false }; + let options = { allowTrailingComma: true }; assertValidParse('{ "hello": [], }', { hello: [] }, options); assertValidParse('{ "hello": [] }', { hello: [] }, options); assertValidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} }, options); assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} }, options); assertValidParse('{ "hello": [1,] }', { hello: [1] }, options); - options = { disallowTrailingComma: true }; + options = { allowTrailingComma: false }; assertInvalidParse('{ "hello": [], }', { hello: [] }, options); assertInvalidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} }, options); }); @@ -272,7 +272,7 @@ suite('JSON', () => { assertTree('{ "val": 1 }', { type: 'object', offset: 0, length: 12, children: [ { - type: 'property', offset: 2, length: 8, columnOffset: 7, children: [ + type: 'property', offset: 2, length: 8, colonOffset: 7, children: [ { type: 'string', offset: 2, length: 5, value: 'val' }, { type: 'number', offset: 9, length: 1, value: 1 } ] @@ -283,13 +283,13 @@ suite('JSON', () => { { type: 'object', offset: 0, length: 32, children: [ { - type: 'property', offset: 1, length: 9, columnOffset: 5, children: [ + type: 'property', offset: 1, length: 9, colonOffset: 5, children: [ { type: 'string', offset: 1, length: 4, value: 'id' }, { type: 'string', offset: 7, length: 3, value: '$' } ] }, { - type: 'property', offset: 12, length: 18, columnOffset: 15, children: [ + type: 'property', offset: 12, length: 18, colonOffset: 15, children: [ { type: 'string', offset: 12, length: 3, value: 'v' }, { type: 'array', offset: 17, length: 13, children: [ @@ -306,12 +306,12 @@ suite('JSON', () => { { type: 'object', offset: 0, length: 27, children: [ { - type: 'property', offset: 3, length: 20, columnOffset: 7, children: [ + type: 'property', offset: 3, length: 20, colonOffset: 7, children: [ { type: 'string', offset: 3, length: 4, value: 'id' }, { type: 'object', offset: 9, length: 14, children: [ { - type: 'property', offset: 11, length: 10, columnOffset: 16, children: [ + type: 'property', offset: 11, length: 10, colonOffset: 16, children: [ { type: 'string', offset: 11, length: 5, value: 'foo' }, { type: 'object', offset: 18, length: 3, children: [] } ] @@ -322,6 +322,6 @@ suite('JSON', () => { } ] } - , [ParseErrorCode.PropertyNameExpected, ParseErrorCode.ValueExpected], { disallowTrailingComma: true }); + , [ParseErrorCode.PropertyNameExpected, ParseErrorCode.ValueExpected], { allowTrailingComma: false }); }); }); diff --git a/src/vs/base/test/common/jsonEdit.test.ts b/src/vs/base/test/common/jsonEdit.test.ts index 87ced6125b0..a7c5978d96b 100644 --- a/src/vs/base/test/common/jsonEdit.test.ts +++ b/src/vs/base/test/common/jsonEdit.test.ts @@ -101,13 +101,13 @@ suite('JSON - edits', () => { content = '//comment'; edits = setProperty(content, ['foo', 0], 'bar', formatterOptions); - assertEdit(content, edits, '{\n "foo": [\n "bar"\n ]\n} //comment\n'); + assertEdit(content, edits, '{\n "foo": [\n "bar"\n ]\n} //comment'); }); test('remove property', () => { let content = '{\n "x": "y"\n}'; let edits = removeProperty(content, ['x'], formatterOptions); - assertEdit(content, edits, '{}'); + assertEdit(content, edits, '{\n}'); content = '{\n "x": "y", "a": []\n}'; edits = removeProperty(content, ['x'], formatterOptions); diff --git a/src/vs/base/test/common/jsonFormatter.test.ts b/src/vs/base/test/common/jsonFormatter.test.ts index 2d15812917a..2ef51e1b383 100644 --- a/src/vs/base/test/common/jsonFormatter.test.ts +++ b/src/vs/base/test/common/jsonFormatter.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; suite('JSON - formatter', () => { function format(content: string, expected: string, insertSpaces = true) { - let range = void 0; + let range: Formatter.Range | undefined = void 0; var rangeStart = content.indexOf('|'); var rangeEnd = content.lastIndexOf('|'); if (rangeStart !== -1 && rangeEnd !== -1) { @@ -344,12 +344,12 @@ suite('JSON - formatter', () => { '{ "a": {},', ' |"b": [null],', '"c": {}', - '} |' + '}|' ].join('\n'); var expected = [ '{ "a": {},', - ' "b": [', + ' "b": [', ' null', ' ],', ' "c": {}', diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 66189159370..1b954752d0d 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -830,7 +830,7 @@ export class WindowsManager implements IWindowsMainService { } private doExtractPathsFromAPI(openConfig: IOpenConfiguration): IPathToOpen[] { - const pathsToOpen = []; + const pathsToOpen: IPathToOpen[] = []; const cli = openConfig.cli; let parseOptions: IPathParseOptions = { gotoLineMode: cli && cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile }; for (const pathToOpen of openConfig.urisToOpen) { @@ -868,7 +868,7 @@ export class WindowsManager implements IWindowsMainService { } private doExtractPathsFromCLI(cli: ParsedArgs): IPath[] { - const pathsToOpen = []; + const pathsToOpen: IPathToOpen[] = []; const parseOptions: IPathParseOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto }; // folder uris diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 727365c539b..36171828096 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -19,6 +19,8 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ITextModel } from 'vs/editor/common/model'; import { IPosition } from 'vs/base/browser/ui/contextview/contextview'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { always } from 'vs/base/common/async'; export type ServicesAccessor = ServicesAccessor; export type IEditorContributionCtor = IConstructorSignature1; @@ -258,13 +260,23 @@ export function registerDefaultLanguageCommand(id: string, handler: (model: ITex } const model = accessor.get(IModelService).getModel(resource); - if (!model) { - throw illegalArgument('Can not find open model for ' + resource); + if (model) { + const editorPosition = Position.lift(position); + return handler(model, editorPosition, args); } - const editorPosition = Position.lift(position); - - return handler(model, editorPosition, args); + return accessor.get(ITextModelService).createModelReference(resource).then(reference => { + return always(new Promise((resolve, reject) => { + try { + let result = handler(reference.object.textEditorModel, Position.lift(position), args); + resolve(result); + } catch (err) { + reject(err); + } + }), () => { + reference.dispose(); + }); + }); }); } diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index 9b08ce87aac..a4628ec508e 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -30,8 +30,8 @@ export class ColorDetector implements IEditorContribution { private _globalToDispose: IDisposable[] = []; private _localToDispose: IDisposable[] = []; - private _computePromise: CancelablePromise; - private _timeoutTimer: TimeoutTimer; + private _computePromise: CancelablePromise | null; + private _timeoutTimer: TimeoutTimer | null; private _decorationsIds: string[] = []; private _colorDatas = new Map(); @@ -108,11 +108,8 @@ export class ColorDetector implements IEditorContribution { return; } const model = this._editor.getModel(); - // if (!model) { - // return; - // } - if (!ColorProviderRegistry.has(model)) { + if (!model || !ColorProviderRegistry.has(model)) { return; } @@ -129,7 +126,13 @@ export class ColorDetector implements IEditorContribution { } private beginCompute(): void { - this._computePromise = createCancelablePromise(token => getColors(this._editor.getModel(), token)); + this._computePromise = createCancelablePromise(token => { + const model = this._editor.getModel(); + if (!model) { + return Promise.resolve([]); + } + return getColors(model, token); + }); this._computePromise.then((colorInfos) => { this.updateDecorations(colorInfos); this.updateColorDecorators(colorInfos); @@ -226,7 +229,12 @@ export class ColorDetector implements IEditorContribution { } getColorData(position: Position): IColorData | null { - const decorations = this._editor.getModel() + const model = this._editor.getModel(); + if (!model) { + return null; + } + + const decorations = model .getDecorationsInRange(Range.fromPositions(position, position)) .filter(d => this._colorDatas.has(d.id)); diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index 0d852cd9ed9..347c430e8a1 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -133,7 +133,11 @@ export class OutlineGroup extends TreeElement { } private _updateMatches(pattern: string, item: OutlineElement, topMatch: OutlineElement): OutlineElement { - item.score = fuzzyScore(pattern, pattern.toLowerCase(), 0, item.symbol.name, item.symbol.name.toLowerCase(), 0, true); + + item.score = pattern + ? fuzzyScore(pattern, pattern.toLowerCase(), 0, item.symbol.name, item.symbol.name.toLowerCase(), 0, true) + : [-100, []]; + if (item.score && (!topMatch || item.score[0] > topMatch.score[0])) { topMatch = item; } @@ -142,7 +146,7 @@ export class OutlineGroup extends TreeElement { topMatch = this._updateMatches(pattern, child, topMatch); if (!item.score && child.score) { // don't filter parents with unfiltered children - item.score = [0, []]; + item.score = [-100, []]; } } return topMatch; diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index eec514c2b15..5a85fbe403f 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -23,7 +23,7 @@ import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegist import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; import { DefinitionAction, DefinitionActionConfig } from './goToDefinitionCommands'; import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDefinition/clickLinkGesture'; -import { IWordAtPosition, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { IWordAtPosition, IModelDeltaDecoration, ITextModel, IFoundBracket } from 'vs/editor/common/model'; import { Position } from 'vs/editor/common/core/position'; class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorContribution { @@ -205,7 +205,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC private getPreviewRangeBasedOnBrackets(textEditorModel: ITextModel, startLineNumber: number) { const maxLineNumber = Math.min(textEditorModel.getLineCount(), startLineNumber + GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES); - const brackets = []; + const brackets: IFoundBracket[] = []; let ignoreFirstEmpty = true; let currentBracket = textEditorModel.findNextBracket(new Position(startLineNumber, 1)); diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 1950a39827c..1849c8d7ae3 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -474,7 +474,9 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { private getParameterLabelOffsets(signature: modes.SignatureInformation, paramIdx: number): [number, number] { const param = signature.parameters[paramIdx]; - if (Array.isArray(param.label)) { + if (!param) { + return [0, 0]; + } else if (Array.isArray(param.label)) { return param.label; } else { const idx = signature.label.lastIndexOf(param.label); diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 68450c4f919..9928428a60c 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -357,6 +357,10 @@ export class SnippetSession { let firstBeforeText = model.getValueInRange(SnippetSession.adjustSelection(model, editor.getSelection(), overwriteBefore, 0)); let firstAfterText = model.getValueInRange(SnippetSession.adjustSelection(model, editor.getSelection(), 0, overwriteAfter)); + // remember the first non-whitespace column to decide if + // `keepWhitespace` should be overruled for secondary selections + let firstLineFirstNonWhitespace = model.getLineFirstNonWhitespaceColumn(editor.getSelection().positionLineNumber); + // sort selections by their start position but remeber // the original index. that allows you to create correct // offset-based selection logic without changing the @@ -387,8 +391,10 @@ export class SnippetSession { // adjust the template string to match the indentation and // whitespace rules of this insert location (can be different for each cursor) + // happens when being asked for (default) or when this is a secondary + // cursor and the leading whitespace is different const start = snippetSelection.getStartPosition(); - if (adjustWhitespace) { + if (adjustWhitespace || (idx > 0 && firstLineFirstNonWhitespace !== model.getLineFirstNonWhitespaceColumn(selection.positionLineNumber))) { SnippetSession.adjustWhitespace(model, start, snippet); } diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index f78a5daae87..69252a4dd81 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -143,17 +143,16 @@ } /** Styles for each row in the list **/ - .monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label::before { - height: 16px; - padding-right: 4px; + height: 100%; + } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon { display: block; height: 16px; width: 16px; - margin-right: 2px; + margin-left: 2px; background-repeat: no-repeat; background-size: 80%; background-position: center; @@ -167,33 +166,39 @@ display: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon { background-image: url('Misc_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.method, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.function, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.constructor { background-image: url('Method_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.field { background-image: url('Field_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.event { background-image: url('Event_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.operator { background-image: url('Operator_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.variable { background-image: url('LocalVariable_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.class { background-image: url('Class_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.interface { background-image: url('Interface_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.struct { background-image: url('Structure_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.type-parameter { background-image: url('Template_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.module { background-image: url('Namespace_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.property { background-image: url('Property_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.unit { background-image: url('Ruler_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.constant { background-image: url('Constant_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.value, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.enum { background-image: url('Enumerator_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.enum-member { background-image: url('EnumItem_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.keyword { background-image: url('IntelliSenseKeyword_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.text { background-image: url('String_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.color { background-image: url('ColorPalette_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.file { background-image: url('Document_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.reference { background-image: url('ImportFile_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.snippet { background-image: url('Snippet_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.customcolor { background-image: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.folder { background-image: url('Folder_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.suggest-icon::before { + content: ' '; + background-image: url('Misc_16x.svg'); + background-repeat: no-repeat; + background-position: center; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method::before, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.function::before, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor::before { background-image: url('Method_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field::before { background-image: url('Field_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event::before { background-image: url('Event_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator::before { background-image: url('Operator_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable::before { background-image: url('LocalVariable_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class::before { background-image: url('Class_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface::before { background-image: url('Interface_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct::before { background-image: url('Structure_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter::before { background-image: url('Template_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module::before { background-image: url('Namespace_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property::before { background-image: url('Property_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit::before { background-image: url('Ruler_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant::before { background-image: url('Constant_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.value::before, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum::before { background-image: url('Enumerator_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member::before { background-image: url('EnumItem_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword::before { background-image: url('IntelliSenseKeyword_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text::before { background-image: url('String_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color::before { background-image: url('ColorPalette_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file::before { background-image: url('Document_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference::before { background-image: url('ImportFile_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet::before { background-image: url('Snippet_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.customcolor::before { background-image: none; } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder::before { background-image: url('Folder_16x.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.customcolor .colorspan { margin: 0 0 0 0.3em; @@ -284,80 +289,80 @@ background-image: url('./close-dark.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon { background-image: url('Misc_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon::before { background-image: url('Misc_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.method, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.method, -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.function, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.function, -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.constructor, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.constructor { background-image: url('Method_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method::before, +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.function::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.function::before, +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor::before { background-image: url('Method_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.field, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.field { background-image: url('Field_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field::before { background-image: url('Field_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.event, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.event { background-image: url('Event_16x_vscode_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event::before { background-image: url('Event_16x_vscode_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.operator, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.operator { background-image: url('Operator_16x_vscode_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator::before { background-image: url('Operator_16x_vscode_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.variable, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.variable { background-image: url('LocalVariable_16x_vscode_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable::before { background-image: url('LocalVariable_16x_vscode_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.class, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.class { background-image: url('Class_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class::before { background-image: url('Class_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.interface, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.interface { background-image: url('Interface_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface::before { background-image: url('Interface_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.struct, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.struct { background-image: url('Structure_16x_vscode_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct::before { background-image: url('Structure_16x_vscode_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.type-parameter, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.type-parameter { background-image: url('Template_16x_vscode_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter::before { background-image: url('Template_16x_vscode_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.module, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.module { background-image: url('Namespace_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module::before { background-image: url('Namespace_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.property, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.property { background-image: url('Property_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property::before { background-image: url('Property_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.unit, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.unit { background-image: url('Ruler_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit::before { background-image: url('Ruler_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.constant, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.constant { background-image: url('Constant_16x_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant::before { background-image: url('Constant_16x_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.value, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.value, -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.enum, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.enum { background-image: url('Enumerator_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.value::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.value::before, +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum::before { background-image: url('Enumerator_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.enum-member, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.enum-member { background-image: url('EnumItem_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member::before { background-image: url('EnumItem_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.keyword, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.keyword { background-image: url('IntelliSenseKeyword_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword::before { background-image: url('IntelliSenseKeyword_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.text, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.text { background-image: url('String_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text::before { background-image: url('String_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.color, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.color { background-image: url('ColorPalette_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color::before { background-image: url('ColorPalette_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.file, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.file { background-image: url('Document_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file::before { background-image: url('Document_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.reference, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.reference { background-image: url('ImportFile_16x_vscode_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference::before { background-image: url('ImportFile_16x_vscode_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.snippet, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.snippet { background-image: url('Snippet_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet::before { background-image: url('Snippet_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.customcolor, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.customcolor { background-image: none; } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.customcolor::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.customcolor::before { background-image: none; } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.folder, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.folder { background-image: url('Folder_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder::before { background-image: url('Folder_inverse_16x.svg'); } diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 8f53272b3ca..3dc19b69164 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -37,6 +37,7 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { FileKind } from 'vs/platform/files/common/files'; const expandSuggestionDocsByDefault = false; const maxSuggestionsToShow = 12; @@ -85,6 +86,7 @@ class Renderer implements IListRendererObject.create(null); data.disposables = []; data.root = container; + addClass(data.root, 'show-file-icons'); data.icon = append(container, $('.icon')); data.colorspan = append(data.icon, $('span.colorspan')); @@ -147,33 +150,42 @@ class Renderer implements IListRenderer { - parseErrors.push({ error: error }); + onError: (error: json.ParseErrorCode, offset: number, length: number) => { + parseErrors.push({ error, offset, length }); } }; if (content) { diff --git a/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts b/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts index e16dc49b2e2..94cba280d74 100644 --- a/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts @@ -85,10 +85,10 @@ export class DiagnosticsService implements IDiagnosticsService { getPerformanceInfo(info: IMainProcessInfo): Promise { return listProcesses(info.mainPID).then(rootProcess => { - const workspaceInfoMessages = []; + const workspaceInfoMessages: string[] = []; // Workspace Stats - const workspaceStatPromises = []; + const workspaceStatPromises: Promise[] = []; if (info.windows.some(window => window.folderURIs && window.folderURIs.length > 0)) { info.windows.forEach(window => { if (window.folderURIs.length === 0) { @@ -173,7 +173,7 @@ export class DiagnosticsService implements IDiagnosticsService { console.log(this.formatProcessList(info, rootProcess)); // Workspace Stats - const workspaceStatPromises = []; + const workspaceStatPromises: Promise[] = []; if (info.windows.some(window => window.folderURIs && window.folderURIs.length > 0)) { console.log(''); console.log('Workspace Stats: '); diff --git a/src/vs/platform/driver/electron-browser/driver.ts b/src/vs/platform/driver/electron-browser/driver.ts index f09c8b4b9ec..7847ac043a0 100644 --- a/src/vs/platform/driver/electron-browser/driver.ts +++ b/src/vs/platform/driver/electron-browser/driver.ts @@ -22,7 +22,7 @@ function serializeElement(element: Element, recursive: boolean): IElement { attributes[attr.name] = attr.value; } - const children = []; + const children: IElement[] = []; if (recursive) { for (let i = 0; i < element.children.length; i++) { @@ -119,7 +119,7 @@ class WindowDriver implements IWindowDriver { const element = document.querySelector(selector); if (element !== document.activeElement) { - const chain = []; + const chain: string[] = []; let el = document.activeElement; while (el) { diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index f308e1471f4..ff392b24669 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -109,7 +109,7 @@ export class Driver implements IDriver, IWindowDriverRegistry { const resolvedKeybinding = new USLayoutResolvedKeybinding(noModifiedKeybinding, OS); const keyCode = resolvedKeybinding.getElectronAccelerator(); - const modifiers = []; + const modifiers: string[] = []; if (keybinding.ctrlKey) { modifiers.push('ctrl'); diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index 6930c70087b..240c532f9a2 100644 --- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts @@ -628,8 +628,8 @@ export class ExtensionGalleryService implements IExtensionGalleryService { .withFilter(FilterType.ExtensionName, ...extensionNames); return this.queryGallery(query, token).then(result => { - const dependencies = []; - const ids = []; + const dependencies: IGalleryExtension[] = []; + const ids: string[] = []; for (let index = 0; index < result.galleryExtensions.length; index++) { const rawExtension = result.galleryExtensions[index]; diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 42dfd9148b2..1c60d7b0334 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -655,7 +655,7 @@ export class ExtensionManagementService extends Disposable implements IExtension return []; } const packedExtensions = installed.filter(i => extension.manifest.extensionPack.some(id => areSameExtensions({ id }, i.galleryIdentifier))); - const packOfPackedExtensions = []; + const packOfPackedExtensions: ILocalExtension[] = []; for (const packedExtension of packedExtensions) { packOfPackedExtensions.push(...this.getAllPackExtensionsToUninstall(packedExtension, installed, checked)); } @@ -827,7 +827,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private filterUninstalled(...ids: string[]): Promise { return this.withUninstalledExtensions(allUninstalled => { - const uninstalled = []; + const uninstalled: string[] = []; for (const id of ids) { if (!!allUninstalled[id]) { uninstalled.push(id); diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index ba3e3e1d31d..41c6352e1c1 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -53,7 +53,7 @@ export interface IFileService { /** * Tries to activate a provider with the given scheme. */ - activateProvider(scheme: string): TPromise; + activateProvider(scheme: string): Thenable; /** * Checks if this file service can handle the given resource. diff --git a/src/vs/platform/request/node/requestService.ts b/src/vs/platform/request/node/requestService.ts index fe394ac1555..d8e9bfdde46 100644 --- a/src/vs/platform/request/node/requestService.ts +++ b/src/vs/platform/request/node/requestService.ts @@ -20,9 +20,9 @@ export class RequestService implements IRequestService { _serviceBrand: any; - private proxyUrl: string; + private proxyUrl?: string; private strictSSL: boolean; - private authorization: string; + private authorization?: string; private disposables: IDisposable[] = []; constructor( @@ -35,7 +35,7 @@ export class RequestService implements IRequestService { private configure(config: IHTTPConfiguration) { this.proxyUrl = config.http && config.http.proxy; - this.strictSSL = config.http && config.http.proxyStrictSSL; + this.strictSSL = !!(config.http && config.http.proxyStrictSSL); this.authorization = config.http && config.http.proxyAuthorization; } @@ -43,7 +43,7 @@ export class RequestService implements IRequestService { this.logService.trace('RequestService#request', options.url); const { proxyUrl, strictSSL } = this; - const agentPromise = options.agent ? Promise.resolve(options.agent) : Promise.resolve(getProxyAgent(options.url, { proxyUrl, strictSSL })); + const agentPromise = options.agent ? Promise.resolve(options.agent) : Promise.resolve(getProxyAgent(options.url || '', { proxyUrl, strictSSL })); return agentPromise.then(agent => { options.agent = agent; diff --git a/src/vs/platform/search/common/search.ts b/src/vs/platform/search/common/search.ts index 14ac2479756..387525b12d9 100644 --- a/src/vs/platform/search/common/search.ts +++ b/src/vs/platform/search/common/search.ts @@ -25,8 +25,9 @@ export const ISearchService = createDecorator('searchService'); */ export interface ISearchService { _serviceBrand: any; - search(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise; - extendQuery(query: ISearchQuery): void; + textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise; + fileSearch(query: IFileQuery, token?: CancellationToken): TPromise; + extendQuery(query: ITextQuery | IFileQuery): void; clearCache(cacheKey: string): TPromise; registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable; } @@ -56,7 +57,8 @@ export const enum SearchProviderType { } export interface ISearchResultProvider { - search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): TPromise; + textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): TPromise; + fileSearch(query: IFileQuery, token?: CancellationToken): TPromise; clearCache(cacheKey: string): TPromise; } @@ -67,52 +69,61 @@ export interface IFolderQuery { fileEncoding?: string; disregardIgnoreFiles?: boolean; disregardGlobalIgnoreFiles?: boolean; + ignoreSymlinks?: boolean; } -export interface ICommonQueryOptions { +export interface ICommonQueryProps { + /** For telemetry - indicates what is triggering the source */ + _reason?: string; + + folderQueries?: IFolderQuery[]; + includePattern?: glob.IExpression; + excludePattern?: glob.IExpression; extraFileResources?: U[]; - filePattern?: string; // file search only - fileEncoding?: string; + + useRipgrep?: boolean; maxResults?: number; + usingSearchPaths?: boolean; +} + +export interface IFileQueryProps extends ICommonQueryProps { + type: QueryType.File; + filePattern?: string; + + // TODO: Remove this! + disregardExcludeSettings?: boolean; + /** * If true no results will be returned. Instead `limitHit` will indicate if at least one result exists or not. - * * Currently does not work with queries including a 'siblings clause'. */ exists?: boolean; sortByScore?: boolean; cacheKey?: string; - useRipgrep?: boolean; - disregardIgnoreFiles?: boolean; - disregardGlobalIgnoreFiles?: boolean; - disregardExcludeSettings?: boolean; - ignoreSymlinks?: boolean; - maxFileSize?: number; - previewOptions?: ITextSearchPreviewOptions; } -export interface IQueryOptions extends ICommonQueryOptions { - excludePattern?: string; - includePattern?: string; -} - -export interface ISearchQueryProps extends ICommonQueryOptions { - type: QueryType; - - excludePattern?: glob.IExpression; - includePattern?: glob.IExpression; +export interface ITextQueryProps extends ICommonQueryProps { + type: QueryType.Text; contentPattern?: IPatternInfo; - folderQueries?: IFolderQuery[]; - usingSearchPaths?: boolean; + + previewOptions?: ITextSearchPreviewOptions; + maxFileSize?: number; + usePCRE2?: boolean; } -export type ISearchQuery = ISearchQueryProps; -export type IRawSearchQuery = ISearchQueryProps; +export type IFileQuery = IFileQueryProps; +export type IRawFileQuery = IFileQueryProps; +export type ITextQuery = ITextQueryProps; +export type IRawTextQuery = ITextQueryProps; + +export type IRawQuery = IRawTextQuery | IRawFileQuery; +export type ISearchQuery = ITextQuery | IFileQuery; export const enum QueryType { File = 1, Text = 2 } + /* __GDPR__FRAGMENT__ "IPatternInfo" : { "pattern" : { "classification": "CustomerContent", "purpose": "FeatureInsight" }, @@ -134,6 +145,10 @@ export interface IPatternInfo { isSmartCase?: boolean; } +export interface IExtendedExtensionSearchOptions { + usePCRE2?: boolean; +} + export interface IFileMatch { resource?: U; matches?: ITextSearchResult[]; @@ -287,6 +302,7 @@ export class OneLineRange extends SearchRange { export interface ISearchConfigurationProperties { exclude: glob.IExpression; useRipgrep: boolean; + disableRipgrep: boolean; /** * Use ignore file for file search. */ @@ -298,6 +314,7 @@ export interface ISearchConfigurationProperties { location: 'sidebar' | 'panel'; useReplacePreview: boolean; showLineNumbers: boolean; + usePCRE2: boolean; } export interface ISearchConfiguration extends IFilesConfiguration { @@ -327,18 +344,18 @@ export function getExcludes(configuration: ISearchConfiguration): glob.IExpressi return allExcludes; } -export function pathIncludedInQuery(query: ISearchQuery, fsPath: string): boolean { - if (query.excludePattern && glob.match(query.excludePattern, fsPath)) { +export function pathIncludedInQuery(queryProps: ICommonQueryProps, fsPath: string): boolean { + if (queryProps.excludePattern && glob.match(queryProps.excludePattern, fsPath)) { return false; } - if (query.includePattern && !glob.match(query.includePattern, fsPath)) { + if (queryProps.includePattern && !glob.match(queryProps.includePattern, fsPath)) { return false; } // If searchPaths are being used, the extra file must be in a subfolder and match the pattern, if present - if (query.usingSearchPaths) { - return !!query.folderQueries && query.folderQueries.every(fq => { + if (queryProps.usingSearchPaths) { + return !!queryProps.folderQueries && queryProps.folderQueries.every(fq => { const searchPath = fq.folder.fsPath; if (paths.isEqualOrParent(fsPath, searchPath)) { return !fq.includePattern || !!glob.match(fq.includePattern, fsPath); diff --git a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts index 5f722afccb4..a3a46751062 100644 --- a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts @@ -8,6 +8,8 @@ import * as uuid from 'vs/base/common/uuid'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; +export const lastSessionDateStorageKey = 'telemetry.lastSessionDate'; + export function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string, version: string, machineId: string, installSourcePath: string): TPromise<{ [name: string]: string }> { return resolveCommonProperties(commit, version, machineId, installSourcePath).then(result => { // __GDPR__COMMON__ "common.version.shell" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } @@ -15,7 +17,7 @@ export function resolveWorkbenchCommonProperties(storageService: IStorageService // __GDPR__COMMON__ "common.version.renderer" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } result['common.version.renderer'] = process.versions && (process).versions['chrome']; - const lastSessionDate = storageService.get('telemetry.lastSessionDate', StorageScope.GLOBAL); + const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL); storageService.store('telemetry.lastSessionDate', new Date().toUTCString(), StorageScope.GLOBAL); // __GDPR__COMMON__ "common.firstSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index 8e2707a6dc4..c77d018131e 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -55,7 +55,7 @@ export interface ITheme { getColor(color: ColorIdentifier, useDefault?: boolean): Color | null; /** - * Returns wheter the theme defines a value for the color. If not, that means the + * Returns whether the theme defines a value for the color. If not, that means the * default color will be used. */ defines(color: ColorIdentifier): boolean; @@ -137,4 +137,4 @@ platform.Registry.add(Extensions.ThemingContribution, themingRegistry); export function registerThemingParticipant(participant: IThemingParticipant): IDisposable { return themingRegistry.onThemeChange(participant); -} \ No newline at end of file +} diff --git a/src/vs/platform/theme/test/electron-browser/colorRegistry.releaseTest.ts b/src/vs/platform/theme/test/electron-browser/colorRegistry.releaseTest.ts index 8be585ab3a7..b9bcc8db83e 100644 --- a/src/vs/platform/theme/test/electron-browser/colorRegistry.releaseTest.ts +++ b/src/vs/platform/theme/test/electron-browser/colorRegistry.releaseTest.ts @@ -35,7 +35,7 @@ interface DescriptionDiff { export const forceColorLoad = [editorMarkerNavigationError, overviewRulerModifiedForeground, STATUS_BAR_DEBUGGING_BACKGROUND, debugExceptionWidgetBackground, debugToolBarBackground, buttonBackground, embeddedEditorBackground]; -export const experimental = []; // 'settings.modifiedItemForeground', 'editorUnnecessary.foreground' ]; +export const experimental: string[] = []; // 'settings.modifiedItemForeground', 'editorUnnecessary.foreground' ]; suite('Color Registry', function () { diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index 95c909de43a..e652ff8cde4 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -19,7 +19,6 @@ import { coalesce } from 'vs/base/common/arrays'; import { createHash } from 'crypto'; import * as json from 'vs/base/common/json'; import * as jsonEdit from 'vs/base/common/jsonEdit'; -import { applyEdit } from 'vs/base/common/jsonFormatter'; import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces'; import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; @@ -226,7 +225,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain let newRawWorkspaceContents = rawWorkspaceContents; const edits = jsonEdit.setProperty(rawWorkspaceContents, ['folders'], storedWorkspace.folders, { insertSpaces: false, tabSize: 4, eol: (isLinux || isMacintosh) ? '\n' : '\r\n' }); edits.forEach(edit => { - newRawWorkspaceContents = applyEdit(rawWorkspaceContents, edit); + newRawWorkspaceContents = jsonEdit.applyEdit(rawWorkspaceContents, edit); }); return writeFile(targetConfigPath, newRawWorkspaceContents).then(() => { diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 3ddad042b08..296f5b46f77 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -111,7 +111,7 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { container = this.viewContainersRegistry.get(EXPLORER); } const registeredViews = ViewsRegistry.getViews(container); - const viewIds = []; + const viewIds: string[] = []; const viewDescriptors = coalesce(entry.value.map((item, index) => { // validate if (viewIds.indexOf(item.id) !== -1) { diff --git a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts index 55bd73db626..1656cb5c4de 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; @@ -45,7 +44,7 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape { return this.writeConfiguration(target, key, undefined, resource); } - private writeConfiguration(target: ConfigurationTarget, key: string, value: any, resource: URI): TPromise { + private writeConfiguration(target: ConfigurationTarget, key: string, value: any, resource: URI): Promise { target = target !== null && target !== undefined ? target : this.deriveConfigurationTarget(key, resource); return this.configurationService.updateValue(key, value, { resource }, target, true); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadSearch.ts b/src/vs/workbench/api/electron-browser/mainThreadSearch.ts index f5e2f7996e9..a29485a24a8 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSearch.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSearch.ts @@ -8,7 +8,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { values } from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IFileMatch, IRawFileMatch2, ISearchComplete, ISearchCompleteStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, QueryType, SearchProviderType } from 'vs/platform/search/common/search'; +import { IFileMatch, IRawFileMatch2, ISearchComplete, ISearchCompleteStats, ISearchProgressItem, ISearchResultProvider, ISearchService, QueryType, SearchProviderType, ITextQuery, IFileQuery } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostSearchShape, IExtHostContext, MainContext, MainThreadSearchShape } from '../node/extHost.protocol'; @@ -68,7 +68,7 @@ class SearchOperation { private static _idPool = 0; constructor( - readonly progress: (match: IFileMatch) => any, + readonly progress?: (match: IFileMatch) => any, readonly id: number = ++SearchOperation._idPool, readonly matches = new Map() ) { @@ -83,7 +83,9 @@ class SearchOperation { this.matches.set(match.resource.toString(), match); } - this.progress(match); + if (this.progress) { + this.progress(match); + } } } @@ -106,7 +108,15 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { dispose(this._registrations); } - search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): TPromise { + fileSearch(query: IFileQuery, token: CancellationToken = CancellationToken.None): TPromise { + return this.doSearch(query, null, token); + } + + textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): TPromise { + return this.doSearch(query, onProgress, token); + } + + doSearch(query: ITextQuery | IFileQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): TPromise { if (isFalsyOrEmpty(query.folderQueries)) { return TPromise.as(undefined); } @@ -116,7 +126,7 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { const searchP = query.type === QueryType.File ? this._proxy.$provideFileSearchResults(this._handle, search.id, query, token) - : this._proxy.$provideTextSearchResults(this._handle, search.id, query.contentPattern, query, token); + : this._proxy.$provideTextSearchResults(this._handle, search.id, query, token); return TPromise.wrap(searchP).then((result: ISearchCompleteStats) => { this._searches.delete(search.id); diff --git a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts index ef702f42e53..50a345b39bb 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts @@ -133,7 +133,7 @@ class TreeViewDataProvider implements ITreeViewDataProvider { } private postGetChildren(elements: ITreeItem[]): ITreeItem[] { - const result = []; + const result: ITreeItem[] = []; if (elements) { for (const element of elements) { this.itemsMap.set(element.handle, element); diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index fbf413cfb3b..b5828cf89b9 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -6,19 +6,18 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; -import { IFolderQuery, IPatternInfo, IQueryOptions, ISearchConfiguration, ISearchProgressItem, ISearchQuery, ISearchService, QueryType } from 'vs/platform/search/common/search'; +import { IFolderQuery, IPatternInfo, ISearchConfiguration, ISearchProgressItem, ISearchService, QueryType, IFileQuery } from 'vs/platform/search/common/search'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; -import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; +import { QueryBuilder, ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; @@ -141,13 +140,18 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return !folderConfig.search.followSymlinks; }); - const query: ISearchQuery = { + // TODO replace wth QueryBuilder + folderQueries.forEach(fq => { + fq.ignoreSymlinks = ignoreSymlinks; + }); + + const query: IFileQuery = { folderQueries, type: QueryType.File, maxResults, disregardExcludeSettings: excludePatternOrDisregardExcludes === false, useRipgrep, - ignoreSymlinks + _reason: 'startFileSearch' }; if (typeof includePattern === 'string') { query.includePattern = { [includePattern]: true }; @@ -159,22 +163,23 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { this._searchService.extendQuery(query); - return this._searchService.search(query, token).then(result => { + return this._searchService.fileSearch(query, token).then(result => { return result.results.map(m => m.resource); }, err => { if (!isPromiseCanceledError(err)) { - return TPromise.wrapError(err); + return Promise.reject(err); } return undefined; }); } - $startTextSearch(pattern: IPatternInfo, options: IQueryOptions, requestId: number, token: CancellationToken): Thenable { + $startTextSearch(pattern: IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Thenable { const workspace = this._contextService.getWorkspace(); const folders = workspace.folders.map(folder => folder.uri); const queryBuilder = this._instantiationService.createInstance(QueryBuilder); const query = queryBuilder.text(pattern, folders, options); + query._reason = 'startTextSearch'; const onProgress = (p: ISearchProgressItem) => { if (p.matches) { @@ -182,13 +187,13 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { } }; - const search = this._searchService.search(query, token, onProgress).then( + const search = this._searchService.textSearch(query, token, onProgress).then( result => { return { limitHit: result.limitHit }; }, err => { if (!isPromiseCanceledError(err)) { - return TPromise.wrapError(err); + return Promise.reject(err); } return undefined; @@ -201,17 +206,18 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { const queryBuilder = this._instantiationService.createInstance(QueryBuilder); const folders = this._contextService.getWorkspace().folders.map(folder => folder.uri); const query = queryBuilder.file(folders, { + _reason: 'checkExists', includePattern: includes.join(', '), exists: true }); - return this._searchService.search(query, token).then( + return this._searchService.fileSearch(query, token).then( result => { return result.limitHit; }, err => { if (!isPromiseCanceledError(err)) { - return TPromise.wrapError(err); + return Promise.reject(err); } return undefined; diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 119dfefdd62..8484f983781 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -40,7 +40,6 @@ import { URI } from 'vs/base/common/uri'; import Severity from 'vs/base/common/severity'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as vscode from 'vscode'; import * as paths from 'vs/base/common/paths'; @@ -363,11 +362,11 @@ export function createApiFactory( return extHostTerminalService.terminals; }, showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Thenable { - let documentPromise: TPromise; + let documentPromise: Promise; if (URI.isUri(documentOrUri)) { - documentPromise = TPromise.wrap(workspace.openTextDocument(documentOrUri)); + documentPromise = Promise.resolve(workspace.openTextDocument(documentOrUri)); } else { - documentPromise = TPromise.wrap(documentOrUri); + documentPromise = Promise.resolve(documentOrUri); } return documentPromise.then(document => { return extHostEditors.showTextDocument(document, columnOrOptions, preserveFocus); @@ -555,9 +554,9 @@ export function createApiFactory( let options = uriOrFileNameOrOptions as { language?: string; content?: string; }; if (typeof uriOrFileNameOrOptions === 'string') { - uriPromise = TPromise.as(URI.file(uriOrFileNameOrOptions)); + uriPromise = Promise.resolve(URI.file(uriOrFileNameOrOptions)); } else if (uriOrFileNameOrOptions instanceof URI) { - uriPromise = TPromise.as(uriOrFileNameOrOptions); + uriPromise = Promise.resolve(uriOrFileNameOrOptions); } else if (!options || typeof options === 'object') { uriPromise = extHostDocuments.createDocumentData(options); } else { @@ -844,7 +843,7 @@ class Extension implements vscode.Extension { } } -export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory): TPromise { +export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory): Promise { return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie)); } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 0736bcc56dd..5044ae0b844 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -24,7 +24,7 @@ import { LabelRules } from 'vs/platform/label/common/label'; import { LogLevel } from 'vs/platform/log/common/log'; import { IMarkerData } from 'vs/platform/markers/common/markers'; import { IPickOptions, IQuickInputButton, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { IPatternInfo, IQueryOptions, IRawFileMatch2, IRawSearchQuery, ISearchCompleteStats } from 'vs/platform/search/common/search'; +import { IPatternInfo, IRawFileMatch2, IRawQuery, ISearchCompleteStats, IRawTextQuery } from 'vs/platform/search/common/search'; import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar'; import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; @@ -41,6 +41,7 @@ import { IProgressOptions, IProgressStep } from 'vs/workbench/services/progress/ import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -483,7 +484,7 @@ export interface ExtHostUrlsShape { export interface MainThreadWorkspaceShape extends IDisposable { $startFileSearch(includePattern: string, includeFolder: URI, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Thenable; - $startTextSearch(query: IPatternInfo, options: IQueryOptions, requestId: number, token: CancellationToken): Thenable; + $startTextSearch(query: IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Thenable; $checkExists(includes: string[], token: CancellationToken): Thenable; $saveAll(includeUntitled?: boolean): Thenable; $updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Thenable; @@ -708,9 +709,9 @@ export interface ExtHostFileSystemShape { } export interface ExtHostSearchShape { - $provideFileSearchResults(handle: number, session: number, query: IRawSearchQuery, token: CancellationToken): Thenable; + $provideFileSearchResults(handle: number, session: number, query: IRawQuery, token: CancellationToken): Thenable; + $provideTextSearchResults(handle: number, session: number, query: IRawTextQuery, token: CancellationToken): Thenable; $clearCache(cacheKey: string): Thenable; - $provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, query: IRawSearchQuery, token: CancellationToken): Thenable; } export interface ExtHostExtensionServiceShape { diff --git a/src/vs/workbench/api/node/extHostComments.ts b/src/vs/workbench/api/node/extHostComments.ts index 9d06aa6a57d..3970fd566ac 100644 --- a/src/vs/workbench/api/node/extHostComments.ts +++ b/src/vs/workbench/api/node/extHostComments.ts @@ -5,7 +5,6 @@ import { asThenable } from 'vs/base/common/async'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as modes from 'vs/editor/common/modes'; import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; import * as extHostTypeConverter from 'vs/workbench/api/node/extHostTypeConverters'; @@ -68,7 +67,7 @@ export class ExtHostComments implements ExtHostCommentsShape { const ran = extHostTypeConverter.Range.to(range); if (!data || !data.document) { - return TPromise.as(null); + return Promise.resolve(null); } const provider = this._documentProviders.get(handle); @@ -82,7 +81,7 @@ export class ExtHostComments implements ExtHostCommentsShape { const ran = extHostTypeConverter.Range.to(range); if (!data || !data.document) { - return TPromise.as(null); + return Promise.resolve(null); } const provider = this._documentProviders.get(handle); @@ -120,7 +119,7 @@ export class ExtHostComments implements ExtHostCommentsShape { $provideDocumentComments(handle: number, uri: UriComponents): Thenable { const data = this._documents.getDocumentData(URI.revive(uri)); if (!data || !data.document) { - return TPromise.as(null); + return Promise.resolve(null); } const provider = this._documentProviders.get(handle); @@ -132,7 +131,7 @@ export class ExtHostComments implements ExtHostCommentsShape { $provideWorkspaceComments(handle: number): Thenable { const provider = this._workspaceProviders.get(handle); if (!provider) { - return TPromise.as(null); + return Promise.resolve(null); } return asThenable(() => { diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 6552de5fc9e..5176cbe1602 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -6,7 +6,6 @@ import * as paths from 'vs/base/common/paths'; import { Schemas } from 'vs/base/common/network'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter } from 'vs/base/common/event'; import { asThenable } from 'vs/base/common/async'; import * as nls from 'vs/nls'; @@ -287,7 +286,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { // RPC methods (ExtHostDebugServiceShape) - public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable { if (args.kind === 'integrated') { @@ -300,7 +299,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { }); } - return new TPromise(resolve => { + return new Promise(resolve => { if (this._integratedTerminalInstance) { this._integratedTerminalInstance.processId.then(pid => { resolve(hasChildprocesses(pid)); @@ -318,7 +317,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { this._integratedTerminalInstance.show(); - return new TPromise((resolve, error) => { + return new Promise((resolve) => { setTimeout(_ => { const command = prepareCommand(args, config); this._integratedTerminalInstance.sendText(command, true); @@ -337,7 +336,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return void 0; } - public $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): TPromise { + public $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise { if (!this._variableResolver) { this._variableResolver = new ExtHostVariableResolverService(this._workspaceService, this._editorsService, this._configurationService); } @@ -353,10 +352,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } }; } - return TPromise.wrap(this._variableResolver.resolveAny(ws, config)); + return Promise.resolve(this._variableResolver.resolveAny(ws, config)); } - public $startDASession(handle: number, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): TPromise { + public $startDASession(handle: number, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Thenable { const mythis = this; return this.getAdapterDescriptor(this._providerByType.get(config.type), sessionDto, folderUri, config).then(adapter => { @@ -429,7 +428,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { }); } - public $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): TPromise { + public $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): Promise { // VS Code -> DA convertToDAPaths(message, source => uriToString(source)); @@ -445,7 +444,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return void 0; } - public $stopDASession(handle: number): TPromise { + public $stopDASession(handle: number): Thenable { const tracker = this._debugAdaptersTrackers.get(handle); this._debugAdaptersTrackers.delete(handle); @@ -526,10 +525,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { public $provideDebugConfigurations(handle: number, folderUri: UriComponents | undefined): Thenable { let provider = this._providerByHandle.get(handle); if (!provider) { - return TPromise.wrapError(new Error('no handler found')); + return Promise.reject(new Error('no handler found')); } if (!provider.provideDebugConfigurations) { - return TPromise.wrapError(new Error('handler has no method provideDebugConfigurations')); + return Promise.reject(new Error('handler has no method provideDebugConfigurations')); } return asThenable(() => provider.provideDebugConfigurations(this.getFolder(folderUri), CancellationToken.None)); } @@ -537,10 +536,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { public $resolveDebugConfiguration(handle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Thenable { let provider = this._providerByHandle.get(handle); if (!provider) { - return TPromise.wrapError(new Error('no handler found')); + return Promise.reject(new Error('no handler found')); } if (!provider.resolveDebugConfiguration) { - return TPromise.wrapError(new Error('handler has no method resolveDebugConfiguration')); + return Promise.reject(new Error('handler has no method resolveDebugConfiguration')); } return asThenable(() => provider.resolveDebugConfiguration(this.getFolder(folderUri), debugConfiguration, CancellationToken.None)); } @@ -548,10 +547,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { public $provideDebugAdapter(handle: number, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Thenable { let provider = this._providerByHandle.get(handle); if (!provider) { - return TPromise.wrapError(new Error('no handler found')); + return Promise.reject(new Error('no handler found')); } if (!provider.debugAdapterExecutable && !provider.provideDebugAdapter) { - return TPromise.wrapError(new Error('handler has no methods provideDebugAdapter or debugAdapterExecutable')); + return Promise.reject(new Error('handler has no methods provideDebugAdapter or debugAdapterExecutable')); } return this.getAdapterDescriptor(provider, this.getSession(sessionDto), folderUri, config); } @@ -602,7 +601,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return false; } - private getDebugAdapterTrackers(sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): TPromise { + private getDebugAdapterTrackers(sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Promise { const session = this.getSession(sessionDto); const folder = this.getFolder(folderUri); @@ -612,7 +611,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { .filter(pair => pair.provider.provideDebugAdapterTracker && (pair.type === type || pair.type === '*')) .map(pair => pair.provider.provideDebugAdapterTracker(session, folder, config, CancellationToken.None)); - return TPromise.join(promises).then(trackers => { + return Promise.all(promises).then(trackers => { if (trackers.length > 0) { return new MultiTracker(trackers); } @@ -624,7 +623,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { // a "debugServer" attribute in the launch config takes precedence if (typeof config.debugServer === 'number') { - return TPromise.wrap(new DebugAdapterServer(config.debugServer)); + return Promise.resolve(new DebugAdapterServer(config.debugServer)); } if (debugConfigProvider) { @@ -649,7 +648,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } // fallback: use executable information from package.json - return TPromise.wrap(ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type)); + return Promise.resolve(ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type)); } private startBreakpoints() { @@ -869,8 +868,8 @@ class DirectDebugAdapter extends AbstractDebugAdapter { } } - startSession(): TPromise { - return TPromise.wrap(void 0); + startSession(): Promise { + return Promise.resolve(void 0); } // VSCode -> DA @@ -878,8 +877,8 @@ class DirectDebugAdapter extends AbstractDebugAdapter { this.transport.sendUp(message); } - stopSession(): TPromise { + stopSession(): Promise { this.transport.stop(); - return TPromise.wrap(void 0); + return Promise.resolve(void 0); } } diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index 05f10aa5768..0e078d1bf47 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -6,12 +6,11 @@ import * as nls from 'vs/nls'; import { IDisposable } from 'vs/base/common/lifecycle'; import Severity from 'vs/base/common/severity'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; const hasOwnProperty = Object.hasOwnProperty; -const NO_OP_VOID_PROMISE = TPromise.wrap(void 0); +const NO_OP_VOID_PROMISE = Promise.resolve(void 0); export interface IExtensionMemento { get(key: string, defaultValue: T): T; @@ -32,7 +31,7 @@ export interface IExtensionContext { * Represents the source code (module) of an extension. */ export interface IExtensionModule { - activate(ctx: IExtensionContext): TPromise; + activate(ctx: IExtensionContext): Promise; deactivate(): void; } @@ -162,7 +161,7 @@ export class FailedExtension extends ActivatedExtension { export interface IExtensionsActivatorHost { showMessage(severity: Severity, message: string): void; - actualActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise; + actualActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise; } export class ExtensionActivatedByEvent { @@ -184,7 +183,7 @@ export class ExtensionsActivator { private readonly _registry: ExtensionDescriptionRegistry; private readonly _host: IExtensionsActivatorHost; - private readonly _activatingExtensions: { [extensionId: string]: TPromise; }; + private readonly _activatingExtensions: { [extensionId: string]: Promise; }; private readonly _activatedExtensions: { [extensionId: string]: ActivatedExtension; }; /** * A map of already activated events to speed things up if the same activation event is triggered multiple times. @@ -210,7 +209,7 @@ export class ExtensionsActivator { return this._activatedExtensions[extensionId]; } - public activateByEvent(activationEvent: string, reason: ExtensionActivationReason): TPromise { + public activateByEvent(activationEvent: string, reason: ExtensionActivationReason): Promise { if (this._alreadyActivatedEvents[activationEvent]) { return NO_OP_VOID_PROMISE; } @@ -220,7 +219,7 @@ export class ExtensionsActivator { }); } - public activateById(extensionId: string, reason: ExtensionActivationReason): TPromise { + public activateById(extensionId: string, reason: ExtensionActivationReason): Promise { let desc = this._registry.getExtensionDescription(extensionId); if (!desc) { throw new Error('Extension `' + extensionId + '` is not known'); @@ -273,15 +272,15 @@ export class ExtensionsActivator { } } - private _activateExtensions(extensionDescriptions: IExtensionDescription[], reason: ExtensionActivationReason, recursionLevel: number): TPromise { + private _activateExtensions(extensionDescriptions: IExtensionDescription[], reason: ExtensionActivationReason, recursionLevel: number): Promise { // console.log(recursionLevel, '_activateExtensions: ', extensionDescriptions.map(p => p.id)); if (extensionDescriptions.length === 0) { - return TPromise.as(void 0); + return Promise.resolve(void 0); } extensionDescriptions = extensionDescriptions.filter((p) => !hasOwnProperty.call(this._activatedExtensions, p.id)); if (extensionDescriptions.length === 0) { - return TPromise.as(void 0); + return Promise.resolve(void 0); } if (recursionLevel > 10) { @@ -292,7 +291,7 @@ export class ExtensionsActivator { const error = new Error('More than 10 levels of dependencies (most likely a dependency loop)'); this._activatedExtensions[extensionDescriptions[i].id] = new FailedExtension(error); } - return TPromise.as(void 0); + return Promise.resolve(void 0); } let greenMap: { [id: string]: IExtensionDescription; } = Object.create(null), @@ -316,7 +315,7 @@ export class ExtensionsActivator { if (red.length === 0) { // Finally reached only leafs! - return TPromise.join(green.map((p) => this._activateExtension(p, reason))).then(_ => void 0); + return Promise.all(green.map((p) => this._activateExtension(p, reason))).then(_ => void 0); } return this._activateExtensions(green, reason, recursionLevel + 1).then(_ => { @@ -324,9 +323,9 @@ export class ExtensionsActivator { }); } - private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise { + private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { if (hasOwnProperty.call(this._activatedExtensions, extensionDescription.id)) { - return TPromise.as(void 0); + return Promise.resolve(void 0); } if (hasOwnProperty.call(this._activatingExtensions, extensionDescription.id)) { diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index f1ff54df8a1..a2d339295f1 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -7,7 +7,6 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { join } from 'path'; import { realpath } from 'vs/base/node/pfs'; import Severity from 'vs/base/common/severity'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; @@ -83,7 +82,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { private readonly _proxy: MainThreadExtensionServiceShape; private readonly _extHostLogService: ExtHostLogService; private _activator: ExtensionsActivator; - private _extensionPathIndex: TPromise>; + private _extensionPathIndex: Promise>; /** * This class is constructed manually because it is a service, so it doesn't use any ctor injection */ @@ -123,7 +122,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } }, - actualActivateExtension: (extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise => { + actualActivateExtension: (extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise => { return this._activateExtension(extensionDescription, reason); } }); @@ -132,7 +131,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { }); } - public onExtensionAPIReady(): TPromise { + public onExtensionAPIReady(): Thenable { return this._barrier.wait(); } @@ -143,7 +142,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return false; } - public activateByEvent(activationEvent: string, startup: boolean): TPromise { + public activateByEvent(activationEvent: string, startup: boolean): Thenable { const reason = new ExtensionActivatedByEvent(startup, activationEvent); if (this._barrier.isOpen()) { return this._activator.activateByEvent(activationEvent, reason); @@ -152,7 +151,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } } - public activateById(extensionId: string, reason: ExtensionActivationReason): TPromise { + public activateById(extensionId: string, reason: ExtensionActivationReason): Thenable { if (this._barrier.isOpen()) { return this._activator.activateById(extensionId, reason); } else { @@ -160,12 +159,12 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } } - public activateByIdWithErrors(extensionId: string, reason: ExtensionActivationReason): TPromise { + public activateByIdWithErrors(extensionId: string, reason: ExtensionActivationReason): Thenable { return this.activateById(extensionId, reason).then(() => { const extension = this._activator.getActivatedExtension(extensionId); if (extension.activationFailed) { // activation failed => bubble up the error as the promise result - return TPromise.wrapError(extension.activationFailedError); + return Promise.reject(extension.activationFailedError); } return void 0; }); @@ -188,7 +187,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } // create trie to enable fast 'filename -> extension id' look up - public getExtensionPathIndex(): TPromise> { + public getExtensionPathIndex(): Promise> { if (!this._extensionPathIndex) { const tree = TernarySearchTree.forPaths(); const extensions = this.getAllExtensionDescriptions().map(ext => { @@ -197,14 +196,14 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } return realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext)); }); - this._extensionPathIndex = TPromise.join(extensions).then(() => tree); + this._extensionPathIndex = Promise.all(extensions).then(() => tree); } return this._extensionPathIndex; } - public deactivate(extensionId: string): TPromise { - let result: TPromise = TPromise.as(void 0); + public deactivate(extensionId: string): Thenable { + let result = Promise.resolve(void 0); if (!this._barrier.isOpen()) { return result; @@ -222,9 +221,9 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { // call deactivate if available try { if (typeof extension.module.deactivate === 'function') { - result = TPromise.wrap(extension.module.deactivate()).then(null, (err) => { + result = Promise.resolve(extension.module.deactivate()).then(null, (err) => { // TODO: Do something with err if this is not the shutdown case - return TPromise.as(void 0); + return Promise.resolve(void 0); }); } } catch (err) { @@ -247,7 +246,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { // --- impl - private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise { + private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => { const activationTimes = activatedExtension.activationTimes; let activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); @@ -259,7 +258,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { }); } - private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise { + private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { let event = getTelemetryActivationEvent(extensionDescription, reason); /* __GDPR__ "activatePlugin" : { @@ -271,36 +270,27 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { this._mainThreadTelemetry.$publicLog('activatePlugin', event); if (!extensionDescription.main) { // Treat the extension as being empty => NOT AN ERROR CASE - return TPromise.as(new EmptyExtension(ExtensionActivationTimes.NONE)); + return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE)); } this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.id} ${JSON.stringify(reason)}`); const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); - return TPromise.join([ + return Promise.all([ loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder), this._loadExtensionContext(extensionDescription) ]).then(values => { return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.id, values[0], values[1], activationTimesBuilder); - }, (errors: any[]) => { - // Avoid failing with an array of errors, fail with a single error - if (errors[0]) { - return TPromise.wrapError(errors[0]); - } - if (errors[1]) { - return TPromise.wrapError(errors[1]); - } - return undefined; }); } - private _loadExtensionContext(extensionDescription: IExtensionDescription): TPromise { + private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { let globalState = new ExtensionMemento(extensionDescription.id, true, this._storage); let workspaceState = new ExtensionMemento(extensionDescription.id, false, this._storage); this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`); - return TPromise.join([ + return Promise.all([ globalState.whenReady, workspaceState.whenReady ]).then(() => { @@ -334,20 +324,20 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { try { activationTimesBuilder.activateCallStart(); logService.trace(`ExtensionService#_callActivateOptional ${extensionId}`); - const activateResult: TPromise = extensionModule.activate.apply(global, [context]); + const activateResult: Thenable = extensionModule.activate.apply(global, [context]); activationTimesBuilder.activateCallStop(); activationTimesBuilder.activateResolveStart(); - return TPromise.as(activateResult).then((value) => { + return Promise.resolve(activateResult).then((value) => { activationTimesBuilder.activateResolveStop(); return value; }); } catch (err) { - return TPromise.wrapError(err); + return Promise.reject(err); } } else { // No activate found => the module is the extension's exports - return TPromise.as(extensionModule); + return Promise.resolve(extensionModule); } } @@ -358,18 +348,18 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } } -function loadCommonJSModule(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise { +function loadCommonJSModule(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { let r: T | null = null; activationTimesBuilder.codeLoadingStart(); logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`); try { r = require.__$__nodeRequire(modulePath); } catch (e) { - return TPromise.wrapError(e); + return Promise.reject(e); } finally { activationTimesBuilder.codeLoadingStop(); } - return TPromise.as(r); + return Promise.resolve(r); } function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any { diff --git a/src/vs/workbench/api/node/extHostQuickOpen.ts b/src/vs/workbench/api/node/extHostQuickOpen.ts index e7c92c50fd4..65e3e92a43c 100644 --- a/src/vs/workbench/api/node/extHostQuickOpen.ts +++ b/src/vs/workbench/api/node/extHostQuickOpen.ts @@ -7,7 +7,6 @@ import { asThenable } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { InputBox, InputBoxOptions, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode'; @@ -43,7 +42,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { // clear state from last invocation this._onDidSelectItem = undefined; - const itemsPromise = >TPromise.wrap(itemsOrItemsPromise); + const itemsPromise = >Promise.resolve(itemsOrItemsPromise); const quickPickWidget = this._proxy.$show({ placeHolder: options && options.placeHolder, @@ -120,7 +119,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { this._proxy.$setError(err); - return TPromise.wrapError(err); + return Promise.reject(err); }); } @@ -145,7 +144,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { this._proxy.$setError(err); - return TPromise.wrapError(err); + return Promise.reject(err); }); } diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index a322633776e..e0bf0ca9a11 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter, once } from 'vs/base/common/event'; import { debounce } from 'vs/base/common/decorators'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; @@ -604,23 +603,23 @@ export class ExtHostSCM implements ExtHostSCMShape { const sourceControl = this._sourceControls.get(sourceControlHandle); if (!sourceControl || !sourceControl.quickDiffProvider) { - return TPromise.as(null); + return Promise.resolve(null); } return asThenable(() => sourceControl.quickDiffProvider.provideOriginalResource(uri, token)); } - $onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise { + $onInputBoxValueChange(sourceControlHandle: number, value: string): Promise { this.logService.trace('ExtHostSCM#$onInputBoxValueChange', sourceControlHandle); const sourceControl = this._sourceControls.get(sourceControlHandle); if (!sourceControl) { - return TPromise.as(null); + return Promise.resolve(null); } sourceControl.inputBox.$onInputBoxValueChange(value); - return TPromise.as(null); + return Promise.resolve(null); } $executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): Thenable { @@ -629,13 +628,13 @@ export class ExtHostSCM implements ExtHostSCMShape { const sourceControl = this._sourceControls.get(sourceControlHandle); if (!sourceControl) { - return TPromise.as(null); + return Promise.resolve(null); } const group = sourceControl.getResourceGroup(groupHandle); if (!group) { - return TPromise.as(null); + return Promise.resolve(null); } return group.$executeResourceCommand(handle); @@ -647,19 +646,19 @@ export class ExtHostSCM implements ExtHostSCMShape { const sourceControl = this._sourceControls.get(sourceControlHandle); if (!sourceControl) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } if (!sourceControl.inputBox.validateInput) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } return asThenable(() => sourceControl.inputBox.validateInput(value, cursorPosition)).then(result => { if (!result) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } - return TPromise.as<[string, number]>([result.message, result.type]); + return Promise.resolve<[string, number]>([result.message, result.type]); }); } @@ -697,6 +696,6 @@ export class ExtHostSCM implements ExtHostSCMShape { }); this._selectedSourceControlHandles = set; - return TPromise.as(null); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts index 239b7bd9c87..ec5fe197568 100644 --- a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts +++ b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts @@ -15,7 +15,7 @@ import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; -import { ICachedSearchStats, IFileIndexProviderStats, IFileMatch, IFileSearchStats, IFolderQuery, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search'; +import { ICachedSearchStats, IFileIndexProviderStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, ISearchCompleteStats } from 'vs/platform/search/common/search'; import { IDirectoryEntry, IDirectoryTree, IInternalFileMatch } from 'vs/workbench/services/search/node/fileSearchManager'; import { QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/node/search'; import * as vscode from 'vscode'; @@ -43,7 +43,7 @@ export class FileIndexSearchEngine { private globalExcludePattern: glob.ParsedExpression; - constructor(private config: ISearchQuery, private provider: vscode.FileIndexProvider) { + constructor(private config: IFileQuery, private provider: vscode.FileIndexProvider) { this.filePattern = config.filePattern; this.includePattern = config.includePattern && glob.parse(config.includePattern); this.maxResults = config.maxResults || null; @@ -111,7 +111,7 @@ export class FileIndexSearchEngine { } errs = errs.filter(e => !!e); - return TPromise.wrapError(errs[0]); + return Promise.reject(errs[0]); }); }); } @@ -190,9 +190,9 @@ export class FileIndexSearchEngine { folder: fq.folder, excludes, includes, - useIgnoreFiles: !this.config.disregardIgnoreFiles, - useGlobalIgnoreFiles: !this.config.disregardGlobalIgnoreFiles, - followSymlinks: !this.config.ignoreSymlinks + useIgnoreFiles: !fq.disregardIgnoreFiles, + useGlobalIgnoreFiles: !fq.disregardGlobalIgnoreFiles, + followSymlinks: !fq.ignoreSymlinks }; } @@ -304,7 +304,7 @@ export class FileIndexSearchManager { private readonly folderCacheKeys = new Map>(); - public fileSearch(config: ISearchQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise { + public fileSearch(config: IFileQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise { if (config.sortByScore) { let sortedSearch = this.trySortedSearchFromCache(config, token); if (!sortedSearch) { @@ -341,7 +341,7 @@ export class FileIndexSearchManager { }); } - private getFolderCacheKey(config: ISearchQuery): string { + private getFolderCacheKey(config: IFileQuery): string { const uri = config.folderQueries[0].folder.toString(); const folderCacheKey = config.cacheKey && `${uri}_${config.cacheKey}`; if (!this.folderCacheKeys.get(config.cacheKey)) { @@ -359,7 +359,7 @@ export class FileIndexSearchManager { }; } - private doSortedSearch(engine: FileIndexSearchEngine, config: ISearchQuery, token: CancellationToken): TPromise { + private doSortedSearch(engine: FileIndexSearchEngine, config: IFileQuery, token: CancellationToken): TPromise { let allResultsPromise = createCancelablePromise>(token => { return this.doSearch(engine, token); }); @@ -381,7 +381,7 @@ export class FileIndexSearchManager { allResultsPromise = this.preventCancellation(allResultsPromise); } - return TPromise.wrap( + return Promise.resolve( allResultsPromise.then(complete => { const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null); const sortSW = (typeof config.maxResults !== 'number' || config.maxResults > 0) && StopWatch.create(); @@ -413,7 +413,7 @@ export class FileIndexSearchManager { return this.caches[cacheKey] = new Cache(); } - private trySortedSearchFromCache(config: ISearchQuery, token: CancellationToken): TPromise { + private trySortedSearchFromCache(config: IFileQuery, token: CancellationToken): TPromise { const folderCacheKey = this.getFolderCacheKey(config); const cache = folderCacheKey && this.caches[folderCacheKey]; if (!cache) { @@ -447,7 +447,7 @@ export class FileIndexSearchManager { return undefined; } - private sortResults(config: IRawSearchQuery, results: IInternalFileMatch[], scorerCache: ScorerCache, token: CancellationToken): TPromise { + private sortResults(config: IFileQuery, results: IInternalFileMatch[], scorerCache: ScorerCache, token: CancellationToken): TPromise { // we use the same compare function that is used later when showing the results using fuzzy scoring // this is very important because we are also limiting the number of results by config.maxResults // and as such we want the top items to be included in this result set if the number of items @@ -555,7 +555,7 @@ export class FileIndexSearchManager { public clearCache(cacheKey: string): TPromise { if (!this.folderCacheKeys.has(cacheKey)) { - return TPromise.wrap(undefined); + return Promise.resolve(undefined); } const expandedKeys = this.folderCacheKeys.get(cacheKey); @@ -563,7 +563,7 @@ export class FileIndexSearchManager { this.folderCacheKeys.delete(cacheKey); - return TPromise.as(undefined); + return Promise.resolve(undefined); } private preventCancellation(promise: CancelablePromise): CancelablePromise { diff --git a/src/vs/workbench/api/node/extHostSearch.ts b/src/vs/workbench/api/node/extHostSearch.ts index 200f05ffa49..c438c7fd386 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -6,17 +6,17 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as extfs from 'vs/base/node/extfs'; import { ILogService } from 'vs/platform/log/common/log'; -import { IFolderQuery, IPatternInfo, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search'; +import { IFileQuery, IFolderQuery, IRawFileQuery, IRawQuery, IRawTextQuery, ISearchCompleteStats, ITextQuery } from 'vs/platform/search/common/search'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { FileIndexSearchManager } from 'vs/workbench/api/node/extHostSearch.fileIndex'; import { FileSearchManager } from 'vs/workbench/services/search/node/fileSearchManager'; +import { IFolderSearch, IRawSearch } from 'vs/workbench/services/search/node/legacy/search'; import { SearchService } from 'vs/workbench/services/search/node/rawSearchService'; import { RipgrepSearchProvider } from 'vs/workbench/services/search/node/ripgrepSearchProvider'; import { OutputChannel } from 'vs/workbench/services/search/node/ripgrepSearchUtils'; -import { IFolderSearch, IRawSearch, isSerializedFileMatch, isSerializedSearchComplete, isSerializedSearchSuccess } from 'vs/workbench/services/search/node/search'; +import { isSerializedFileMatch, isSerializedSearchComplete, isSerializedSearchSuccess } from 'vs/workbench/services/search/node/search'; import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager'; import * as vscode from 'vscode'; import { ExtHostSearchShape, IMainContext, MainContext, MainThreadSearchShape } from './extHost.protocol'; @@ -29,8 +29,11 @@ export class ExtHostSearch implements ExtHostSearchShape { private readonly _proxy: MainThreadSearchShape; private readonly _textSearchProvider = new Map(); + private readonly _textSearchUsedSchemes = new Set(); private readonly _fileSearchProvider = new Map(); + private readonly _fileSearchUsedSchemes = new Set(); private readonly _fileIndexProvider = new Map(); + private readonly _fileIndexUsedSchemes = new Set(); private _handlePool: number = 0; private _internalFileSearchHandle: number; @@ -55,20 +58,32 @@ export class ExtHostSearch implements ExtHostSearchShape { } registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider): IDisposable { + if (this._textSearchUsedSchemes.has(scheme)) { + throw new Error(`a provider for the scheme '${scheme}' is already registered`); + } + + this._textSearchUsedSchemes.add(scheme); const handle = this._handlePool++; this._textSearchProvider.set(handle, provider); this._proxy.$registerTextSearchProvider(handle, this._transformScheme(scheme)); return toDisposable(() => { + this._textSearchUsedSchemes.delete(scheme); this._textSearchProvider.delete(handle); this._proxy.$unregisterProvider(handle); }); } registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider): IDisposable { + if (this._fileSearchUsedSchemes.has(scheme)) { + throw new Error(`a provider for the scheme '${scheme}' is already registered`); + } + + this._fileSearchUsedSchemes.add(scheme); const handle = this._handlePool++; this._fileSearchProvider.set(handle, provider); this._proxy.$registerFileSearchProvider(handle, this._transformScheme(scheme)); return toDisposable(() => { + this._fileSearchUsedSchemes.delete(scheme); this._fileSearchProvider.delete(handle); this._proxy.$unregisterProvider(handle); }); @@ -86,16 +101,22 @@ export class ExtHostSearch implements ExtHostSearchShape { } registerFileIndexProvider(scheme: string, provider: vscode.FileIndexProvider): IDisposable { + if (this._fileIndexUsedSchemes.has(scheme)) { + throw new Error(`a provider for the scheme '${scheme}' is already registered`); + } + + this._fileIndexUsedSchemes.add(scheme); const handle = this._handlePool++; this._fileIndexProvider.set(handle, provider); this._proxy.$registerFileIndexProvider(handle, this._transformScheme(scheme)); return toDisposable(() => { + this._fileIndexUsedSchemes.delete(scheme); this._fileSearchProvider.delete(handle); this._proxy.$unregisterProvider(handle); // TODO@roblou - unregisterFileIndexProvider }); } - $provideFileSearchResults(handle: number, session: number, rawQuery: IRawSearchQuery, token: CancellationToken): Thenable { + $provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: CancellationToken): Thenable { const query = reviveQuery(rawQuery); if (handle === this._internalFileSearchHandle) { return this.doInternalFileSearch(handle, session, query, token); @@ -114,24 +135,21 @@ export class ExtHostSearch implements ExtHostSearchShape { } } - private doInternalFileSearch(handle: number, session: number, rawQuery: ISearchQuery, token: CancellationToken): Thenable { + private doInternalFileSearch(handle: number, session: number, rawQuery: IFileQuery, token: CancellationToken): Thenable { return new Promise((resolve, reject) => { const query: IRawSearch = { folderQueries: [], - ignoreSymlinks: rawQuery.ignoreSymlinks, + ignoreSymlinks: rawQuery.folderQueries.some(fq => fq.ignoreSymlinks), filePattern: rawQuery.filePattern, excludePattern: rawQuery.excludePattern, includePattern: rawQuery.includePattern, - contentPattern: rawQuery.contentPattern, maxResults: rawQuery.maxResults, exists: rawQuery.exists, sortByScore: rawQuery.sortByScore, cacheKey: rawQuery.cacheKey, - maxFilesize: rawQuery.maxFileSize, useRipgrep: rawQuery.useRipgrep, - disregardIgnoreFiles: rawQuery.disregardIgnoreFiles, - previewOptions: rawQuery.previewOptions, - disregardGlobalIgnoreFiles: rawQuery.disregardGlobalIgnoreFiles + disregardIgnoreFiles: rawQuery.folderQueries.some(fq => fq.disregardIgnoreFiles), + disregardGlobalIgnoreFiles: rawQuery.folderQueries.some(fq => fq.disregardGlobalIgnoreFiles), }; query.folderQueries = rawQuery.folderQueries.map(fq => ({ disregardGlobalIgnoreFiles: fq.disregardGlobalIgnoreFiles, @@ -180,14 +198,14 @@ export class ExtHostSearch implements ExtHostSearchShape { return this._fileIndexSearchManager.clearCache(cacheKey); } - $provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, rawQuery: IRawSearchQuery, token: CancellationToken): Thenable { + $provideTextSearchResults(handle: number, session: number, rawQuery: IRawTextQuery, token: CancellationToken): Thenable { const provider = this._textSearchProvider.get(handle); if (!provider.provideTextSearchResults) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } const query = reviveQuery(rawQuery); - const engine = new TextSearchManager(pattern, query, provider, this._extfs); + const engine = new TextSearchManager(query, provider, this._extfs); return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress), token); } } @@ -201,9 +219,9 @@ function registerEHProviders(extHostSearch: ExtHostSearch, logService: ILogServi } } -function reviveQuery(rawQuery: IRawSearchQuery): ISearchQuery { +function reviveQuery(rawQuery: U): U extends IRawTextQuery ? ITextQuery : IFileQuery { return { - ...rawQuery, + ...rawQuery, // TODO ...{ folderQueries: rawQuery.folderQueries && rawQuery.folderQueries.map(reviveFolderQuery), extraFileResources: rawQuery.extraFileResources && rawQuery.extraFileResources.map(components => URI.revive(components)) diff --git a/src/vs/workbench/api/node/extHostTreeViews.ts b/src/vs/workbench/api/node/extHostTreeViews.ts index 9614729d309..a8333e2f958 100644 --- a/src/vs/workbench/api/node/extHostTreeViews.ts +++ b/src/vs/workbench/api/node/extHostTreeViews.ts @@ -8,7 +8,6 @@ import * as vscode from 'vscode'; import { basename } from 'vs/base/common/paths'; import { URI } from 'vs/base/common/uri'; import { debounceEvent, Emitter, Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Disposable } from 'vs/base/common/lifecycle'; import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol'; import { ITreeItem, TreeViewItemHandleArg } from 'vs/workbench/common/views'; @@ -70,7 +69,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { $getChildren(treeViewId: string, treeItemHandle?: string): Thenable { const treeView = this.treeViews.get(treeViewId); if (!treeView) { - return TPromise.wrapError(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId))); + return Promise.reject(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId))); } return treeView.getChildren(treeItemHandle); } @@ -144,7 +143,7 @@ class ExtHostTreeView extends Disposable { private _onDidChangeVisibility: Emitter = this._register(new Emitter()); readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; - private refreshPromise: TPromise = TPromise.as(null); + private refreshPromise: Promise = Promise.resolve(null); constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService) { super(); @@ -154,7 +153,7 @@ class ExtHostTreeView extends Disposable { this._register(debounceEvent(this.dataProvider.onDidChangeTreeData, (last, current) => { if (!refreshingPromise) { // New refresh has started - refreshingPromise = new TPromise((c, e) => promiseCallback = c); + refreshingPromise = new Promise(c => promiseCallback = c); this.refreshPromise = this.refreshPromise.then(() => refreshingPromise); } return last ? [...last, current] : [current]; @@ -170,11 +169,11 @@ class ExtHostTreeView extends Disposable { const parentElement = parentHandle ? this.getExtensionElement(parentHandle) : void 0; if (parentHandle && !parentElement) { console.error(`No tree item with id \'${parentHandle}\' found.`); - return TPromise.as([]); + return Promise.resolve([]); } const childrenNodes = this.getChildrenNodes(parentHandle); // Get it from cache - return (childrenNodes ? TPromise.as(childrenNodes) : this.fetchChildrenNodes(parentElement)) + return (childrenNodes ? Promise.resolve(childrenNodes) : this.fetchChildrenNodes(parentElement)) .then(nodes => nodes.map(n => n.item)); } @@ -182,13 +181,13 @@ class ExtHostTreeView extends Disposable { return this.elements.get(treeItemHandle); } - reveal(element: T, options?: { select?: boolean, focus?: boolean }): TPromise { + reveal(element: T, options?: { select?: boolean, focus?: boolean }): Promise { options = options ? options : { select: true, focus: false }; const select = isUndefinedOrNull(options.select) ? true : options.select; const focus = isUndefinedOrNull(options.focus) ? false : options.focus; if (typeof this.dataProvider.getParent !== 'function') { - return TPromise.wrapError(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' method`)); + return Promise.reject(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' method`)); } return this.refreshPromise .then(() => this.resolveUnknownParentChain(element)) @@ -225,7 +224,7 @@ class ExtHostTreeView extends Disposable { return this.resolveParent(element) .then((parent) => { if (!parent) { - return TPromise.as([]); + return Promise.resolve([]); } return this.resolveUnknownParentChain(parent) .then(result => this.resolveTreeNode(parent, result[result.length - 1]) @@ -239,7 +238,7 @@ class ExtHostTreeView extends Disposable { private resolveParent(element: T): Thenable { const node = this.nodes.get(element); if (node) { - return TPromise.as(node.parent ? this.elements.get(node.parent.item.handle) : null); + return Promise.resolve(node.parent ? this.elements.get(node.parent.item.handle) : null); } return asThenable(() => this.dataProvider.getParent(element)); } @@ -253,7 +252,7 @@ class ExtHostTreeView extends Disposable { if (cachedElement) { const node = this.nodes.get(cachedElement); if (node) { - return TPromise.as(node); + return Promise.resolve(node); } } throw new Error(`Cannot resolve tree item for element ${handle}`); @@ -280,7 +279,7 @@ class ExtHostTreeView extends Disposable { const parentNode = parentElement ? this.nodes.get(parentElement) : void 0; return asThenable(() => this.dataProvider.getChildren(parentElement)) - .then(elements => TPromise.join( + .then(elements => Promise.all( (elements || []) .filter(element => !!element) .map(element => asThenable(() => this.dataProvider.getTreeItem(element)) @@ -299,7 +298,7 @@ class ExtHostTreeView extends Disposable { return this.refreshHandles(handlesToRefresh); } } - return TPromise.as(null); + return Promise.resolve(null); } private getHandlesToRefresh(elements: T[]): TreeItemHandle[] { @@ -332,9 +331,9 @@ class ExtHostTreeView extends Disposable { return handlesToUpdate; } - private refreshHandles(itemHandles: TreeItemHandle[]): TPromise { + private refreshHandles(itemHandles: TreeItemHandle[]): Promise { const itemsToRefresh: { [treeItemHandle: string]: ITreeItem } = {}; - return TPromise.join(itemHandles.map(treeItemHandle => + return Promise.all(itemHandles.map(treeItemHandle => this.refreshNode(treeItemHandle) .then(node => { if (node) { diff --git a/src/vs/workbench/api/node/extHostUrls.ts b/src/vs/workbench/api/node/extHostUrls.ts index f8b7b37f9f4..23bfb2e3d6c 100644 --- a/src/vs/workbench/api/node/extHostUrls.ts +++ b/src/vs/workbench/api/node/extHostUrls.ts @@ -6,7 +6,6 @@ import * as vscode from 'vscode'; import { MainContext, IMainContext, ExtHostUrlsShape, MainThreadUrlsShape } from './extHost.protocol'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { toDisposable } from 'vs/base/common/lifecycle'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -45,7 +44,7 @@ export class ExtHostUrls implements ExtHostUrlsShape { const handler = this.handlers.get(handle); if (!handler) { - return TPromise.as(null); + return Promise.resolve(null); } try { handler.handleUri(URI.revive(uri)); @@ -53,6 +52,6 @@ export class ExtHostUrls implements ExtHostUrlsShape { onUnexpectedError(err); } - return TPromise.as(null); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index 4b44c2e6600..4b22d0c281e 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -5,7 +5,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import { EditorViewColumn } from 'vs/workbench/api/shared/editor'; import * as vscode from 'vscode'; @@ -311,7 +310,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { panel.dispose(); this._webviewPanels.delete(handle); } - return TPromise.as(void 0); + return Promise.resolve(void 0); } $deserializeWebviewPanel( @@ -324,7 +323,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { ): Thenable { const serializer = this._serializers.get(viewType); if (!serializer) { - return TPromise.wrapError(new Error(`No serializer found for '${viewType}'`)); + return Promise.reject(new Error(`No serializer found for '${viewType}'`)); } const webview = new ExtHostWebview(webviewHandle, this._proxy, options); diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index 08a022a9fad..01afd29ffc2 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -16,13 +16,14 @@ import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ILogService } from 'vs/platform/log/common/log'; import { Severity } from 'vs/platform/notification/common/notification'; -import { IQueryOptions, IRawFileMatch2 } from 'vs/platform/search/common/search'; +import { IRawFileMatch2 } from 'vs/platform/search/common/search'; import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Range, RelativePattern } from 'vs/workbench/api/node/extHostTypes'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import * as vscode from 'vscode'; import { ExtHostWorkspaceShape, IMainContext, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; function isFolderEqual(folderA: URI, folderB: URI): boolean { return isEqual(folderA, folderB, !isLinux); @@ -399,7 +400,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { } : options.previewOptions; - const queryOptions: IQueryOptions = { + const queryOptions: ITextQueryBuilderOptions = { ignoreSymlinks: typeof options.followSymlinks === 'boolean' ? !options.followSymlinks : undefined, disregardIgnoreFiles: typeof options.useIgnoreFiles === 'boolean' ? !options.useIgnoreFiles : undefined, disregardGlobalIgnoreFiles: typeof options.useGlobalIgnoreFiles === 'boolean' ? !options.useGlobalIgnoreFiles : undefined, diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index 06d97406eb4..18f21d183f1 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -95,10 +95,8 @@ export abstract class Composite extends Component implements IComposite { * Note that DOM-dependent calculations should be performed from the setVisible() * call. Only then the composite will be part of the DOM. */ - create(parent: HTMLElement): Promise { + create(parent: HTMLElement): void { this.parent = parent; - - return Promise.resolve(null); } updateStyles(): void { diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 34c0b5ae2ba..cc8530fec42 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -26,6 +26,7 @@ import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart'; import { getZoomFactor } from 'vs/base/browser/browser'; import { RunOnceScheduler } from 'vs/base/common/async'; +import * as perf from 'vs/base/common/performance'; const TITLE_BAR_HEIGHT = isMacintosh ? 22 : 30; const STATUS_BAR_HEIGHT = 22; @@ -557,6 +558,20 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr position(this.workbenchContainer, 0, 0, 0, 0, 'relative'); size(this.workbenchContainer, this.workbenchSize.width, this.workbenchSize.height); + // Bug on Chrome: Sometimes Chrome wants to scroll the workbench container on layout changes. The fix is to reset scrolling in this case. + // uses set time to ensure this happens in th next frame (RAF will be at the end of this JS time slice and we don't want that) + setTimeout(() => { + perf.mark('willCheckAndFixWorkbenchLayout'); + const workbenchContainer = this.workbenchContainer; + if (workbenchContainer.scrollTop > 0) { + workbenchContainer.scrollTop = 0; + } + if (workbenchContainer.scrollLeft > 0) { + workbenchContainer.scrollLeft = 0; + } + perf.mark('didCheckAndFixWorkbenchLayout'); + }); + // Title Part const titleContainer = this.parts.titlebar.getContainer(); if (isTitlebarHidden) { diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index e41c2c1c501..d6468e0f13f 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -212,8 +212,6 @@ export abstract class CompositePart extends Part { // Remember this.lastActiveCompositeId = this.activeComposite.getId(); - let createCompositePromise: TPromise; - // Composites created for the first time let compositeContainer = this.mapCompositeToCompositeContainer[composite.getId()]; if (!compositeContainer) { @@ -223,92 +221,83 @@ export abstract class CompositePart extends Part { addClasses(compositeContainer, this.compositeCSSClass); compositeContainer.id = composite.getId(); - createCompositePromise = composite.create(compositeContainer).then(() => { - composite.updateStyles(); - }); + composite.create(compositeContainer); + composite.updateStyles(); // Remember composite container this.mapCompositeToCompositeContainer[composite.getId()] = compositeContainer; } - // Composite already exists but is hidden - else { - createCompositePromise = Promise.resolve(null); - } - // Report progress for slow loading composites (but only if we did not create the composites before already) const progressService = this.mapProgressServiceToComposite[composite.getId()]; if (progressService && !compositeContainer) { - this.mapProgressServiceToComposite[composite.getId()].showWhile(createCompositePromise, this.partService.isCreated() ? 800 : 3200 /* less ugly initial startup */); + this.mapProgressServiceToComposite[composite.getId()].showWhile(Promise.resolve(), this.partService.isCreated() ? 800 : 3200 /* less ugly initial startup */); } // Fill Content and Actions - return createCompositePromise.then(() => { + // Make sure that the user meanwhile did not open another composite or closed the part containing the composite + if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) { + return void 0; + } + + // Take Composite on-DOM and show + this.getContentArea().appendChild(compositeContainer); + show(compositeContainer); + + // Setup action runner + this.toolBar.actionRunner = composite.getActionRunner(); + + // Update title with composite title if it differs from descriptor + const descriptor = this.registry.getComposite(composite.getId()); + if (descriptor && descriptor.name !== composite.getTitle()) { + this.updateTitle(composite.getId(), composite.getTitle()); + } + + // Handle Composite Actions + let actionsBinding = this.mapActionsBindingToComposite[composite.getId()]; + if (!actionsBinding) { + actionsBinding = this.collectCompositeActions(composite); + this.mapActionsBindingToComposite[composite.getId()] = actionsBinding; + } + actionsBinding(); + + if (this.telemetryActionsListener) { + this.telemetryActionsListener.dispose(); + this.telemetryActionsListener = null; + } + + // Action Run Handling + this.telemetryActionsListener = this.toolBar.actionRunner.onDidRun(e => { + + // Check for Error + if (e.error && !errors.isPromiseCanceledError(e.error)) { + this.notificationService.error(e.error); + } + + // Log in telemetry + if (this.telemetryService) { + /* __GDPR__ + "workbenchActionExecuted" : { + "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('workbenchActionExecuted', { id: e.action.id, from: this.nameForTelemetry }); + } + }); + + // Indicate to composite that it is now visible + return composite.setVisible(true).then(() => { // Make sure that the user meanwhile did not open another composite or closed the part containing the composite if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) { - return void 0; + return; } - // Take Composite on-DOM and show - this.getContentArea().appendChild(compositeContainer); - show(compositeContainer); - - // Setup action runner - this.toolBar.actionRunner = composite.getActionRunner(); - - // Update title with composite title if it differs from descriptor - const descriptor = this.registry.getComposite(composite.getId()); - if (descriptor && descriptor.name !== composite.getTitle()) { - this.updateTitle(composite.getId(), composite.getTitle()); + // Make sure the composite is layed out + if (this.contentAreaSize) { + composite.layout(this.contentAreaSize); } - - // Handle Composite Actions - let actionsBinding = this.mapActionsBindingToComposite[composite.getId()]; - if (!actionsBinding) { - actionsBinding = this.collectCompositeActions(composite); - this.mapActionsBindingToComposite[composite.getId()] = actionsBinding; - } - actionsBinding(); - - if (this.telemetryActionsListener) { - this.telemetryActionsListener.dispose(); - this.telemetryActionsListener = null; - } - - // Action Run Handling - this.telemetryActionsListener = this.toolBar.actionRunner.onDidRun(e => { - - // Check for Error - if (e.error && !errors.isPromiseCanceledError(e.error)) { - this.notificationService.error(e.error); - } - - // Log in telemetry - if (this.telemetryService) { - /* __GDPR__ - "workbenchActionExecuted" : { - "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('workbenchActionExecuted', { id: e.action.id, from: this.nameForTelemetry }); - } - }); - - // Indicate to composite that it is now visible - return composite.setVisible(true).then(() => { - - // Make sure that the user meanwhile did not open another composite or closed the part containing the composite - if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) { - return; - } - - // Make sure the composite is layed out - if (this.contentAreaSize) { - composite.layout(this.contentAreaSize); - } - }); }, error => this.onError(error)); } diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index 9a811cbe56b..5b3c99d4c0a 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -105,15 +105,11 @@ export abstract class BaseEditor extends Panel implements IEditor { this._options = options; } - create(parent: HTMLElement): void; // create is sync for editors - create(parent: HTMLElement): Promise; - create(parent: HTMLElement): Promise { - const res = super.create(parent); + create(parent: HTMLElement): void { + super.create(parent); // Create Editor this.createEditor(parent); - - return res; } /** @@ -308,4 +304,4 @@ export class EditorMemento implements IEditorMemento { }); }); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 6132c599e14..8d0974b7729 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -210,16 +210,23 @@ export class NotificationsToasts extends Themable { disposables.push(addDisposableListener(notificationToastContainer, EventType.MOUSE_OUT, () => isMouseOverToast = false)); // Install Timers - let timeoutHandle: any; + let purgeTimeoutHandle: any; + let pendingPurgeTimeoutHandle: any; const hideAfterTimeout = () => { - timeoutHandle = setTimeout(() => { + purgeTimeoutHandle = setTimeout(() => { if ( item.sticky || // never hide sticky notifications notificationList.hasFocus() || // never hide notifications with focus isMouseOverToast || // never hide notifications under mouse !this.windowHasFocus // never hide when window has no focus ) { - hideAfterTimeout(); + // If the notification should not be hidden yet for the reasons outlined + // above, we delay an additional check by at least PURGE_TIMEOUT so that + // if the condition changes to hide the notification, the timeout will + // be at least PURGE_TIMEOUT (+ the time it took to change the state) + pendingPurgeTimeoutHandle = setTimeout(() => { + hideAfterTimeout(); + }, NotificationsToasts.PURGE_TIMEOUT[item.severity]); } else { this.removeToast(item); } @@ -228,7 +235,8 @@ export class NotificationsToasts extends Themable { hideAfterTimeout(); - disposables.push(toDisposable(() => clearTimeout(timeoutHandle))); + disposables.push(toDisposable(() => clearTimeout(purgeTimeoutHandle))); + disposables.push(toDisposable(() => clearTimeout(pendingPurgeTimeoutHandle))); } private removeToast(item: INotificationViewItem): void { diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 492d1ef3b3f..41b6122cbc9 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -694,7 +694,7 @@ export class MenubarControl extends Disposable { this.insertActionsBefore(action, target); if (action instanceof SubmenuItemAction) { const submenu = this.menuService.createMenu(action.item.submenu, this.contextKeyService); - const submenuActions = []; + const submenuActions: SubmenuAction[] = []; updateActions(submenu, submenuActions); target.push(new SubmenuAction(action.label, submenuActions)); } else { diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index b2c0e44e3ee..b4abbb8fc87 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -35,6 +35,7 @@ import { WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { localize } from 'vs/nls'; +import { timeout } from 'vs/base/common/async'; export class CustomTreeViewPanel extends ViewletPanel { @@ -207,7 +208,8 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { @IWorkbenchThemeService private themeService: IWorkbenchThemeService, @IInstantiationService private instantiationService: IInstantiationService, @ICommandService private commandService: ICommandService, - @IConfigurationService private configurationService: IConfigurationService + @IConfigurationService private configurationService: IConfigurationService, + @IProgressService2 private progressService: IProgressService2 ) { super(); this.root = new Root(); @@ -240,7 +242,7 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { }); } }; - DOM.removeClass(this.domNode, 'message'); + this.hideMessage(); this.refresh(); } else { this._dataProvider = null; @@ -341,6 +343,10 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { this.message.innerText = message; } + private hideMessage(): void { + DOM.removeClass(this.domNode, 'message'); + } + layout(size: number) { this.domNode.style.height = size + 'px'; if (this.tree) { @@ -351,7 +357,7 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { getOptimalWidth(): number { if (this.tree) { const parentNode = this.tree.getHTMLElement(); - const childNodes = [].slice.call(parentNode.querySelectorAll('.outline-item-label > a')); + const childNodes = ([] as Element[]).slice.call(parentNode.querySelectorAll('.outline-item-label > a')); return DOM.getLargestChildWidth(parentNode, childNodes); } return 0; @@ -401,8 +407,15 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { } private activate() { + this.hideMessage(); if (!this.activated) { - this.extensionService.activateByEvent(`onView:${this.id}`); + this.progressService.withProgress({ location: this.container }, () => this.extensionService.activateByEvent(`onView:${this.id}`)) + .then(() => timeout(2000)) + .then(() => { + if (!this.dataProvider) { + this.showMessage(noDataProviderMessage); + } + }); this.activated = true; } } diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index 2e805f980fa..afd9c52bf6a 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -211,12 +211,11 @@ export class PanelViewlet extends Viewlet { super(id, configurationService, partService, telemetryService, themeService, storageService); } - create(parent: HTMLElement): Promise { - return super.create(parent).then(() => { - this.panelview = this._register(new PanelView(parent, this.options)); - this._register(this.panelview.onDidDrop(({ from, to }) => this.movePanel(from as ViewletPanel, to as ViewletPanel))); - this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e)))); - }); + create(parent: HTMLElement): void { + super.create(parent); + this.panelview = this._register(new PanelView(parent, this.options)); + this._register(this.panelview.onDidDrop(({ from, to }) => this.movePanel(from as ViewletPanel, to as ViewletPanel))); + this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e)))); } private showContextMenu(event: StandardMouseEvent): void { diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 234d6789aa4..bddac305f4b 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -143,31 +143,30 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView this._register(toDisposable(() => this.viewDisposables = dispose(this.viewDisposables))); } - create(parent: HTMLElement): Promise { - return super.create(parent).then(() => { - this._register(this.onDidSashChange(() => this.saveViewSizes())); - this.viewsModel.onDidAdd(added => this.onDidAddViews(added)); - this.viewsModel.onDidRemove(removed => this.onDidRemoveViews(removed)); - const addedViews: IAddedViewDescriptorRef[] = this.viewsModel.visibleViewDescriptors.map((viewDescriptor, index) => { - const size = this.viewsModel.getSize(viewDescriptor.id); - const collapsed = this.viewsModel.isCollapsed(viewDescriptor.id); - return ({ viewDescriptor, index, size, collapsed }); - }); - if (addedViews.length) { - this.onDidAddViews(addedViews); - } - - // Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609 - this.extensionService.whenInstalledExtensionsRegistered().then(() => { - this.areExtensionsReady = true; - if (this.panels.length) { - this.updateTitleArea(); - this.updateViewHeaders(); - } - }); - - this.focus(); + create(parent: HTMLElement): void { + super.create(parent); + this._register(this.onDidSashChange(() => this.saveViewSizes())); + this.viewsModel.onDidAdd(added => this.onDidAddViews(added)); + this.viewsModel.onDidRemove(removed => this.onDidRemoveViews(removed)); + const addedViews: IAddedViewDescriptorRef[] = this.viewsModel.visibleViewDescriptors.map((viewDescriptor, index) => { + const size = this.viewsModel.getSize(viewDescriptor.id); + const collapsed = this.viewsModel.isCollapsed(viewDescriptor.id); + return ({ viewDescriptor, index, size, collapsed }); }); + if (addedViews.length) { + this.onDidAddViews(addedViews); + } + + // Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609 + this.extensionService.whenInstalledExtensionsRegistered().then(() => { + this.areExtensionsReady = true; + if (this.panels.length) { + this.updateTitleArea(); + this.updateViewHeaders(); + } + }); + + this.focus(); } getContextMenuActions(): IAction[] { diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index fafbc3cd78d..94284a6052a 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -136,7 +136,7 @@ export class ShowViewletAction extends Action { this.enabled = !!this.viewletService && !!this.editorGroupService; } - run(): Promise { + run(): Thenable { // Pass focus to viewlet if not open or focused if (this.otherViewletShowing() || !this.sidebarHasFocus()) { diff --git a/src/vs/workbench/buildfile.js b/src/vs/workbench/buildfile.js index 1dfb66c06b4..b9f60119de5 100644 --- a/src/vs/workbench/buildfile.js +++ b/src/vs/workbench/buildfile.js @@ -22,7 +22,7 @@ exports.collectModules = function () { createModuleDescription('vs/workbench/parts/debug/node/telemetryApp', []), createModuleDescription('vs/workbench/services/search/node/searchApp', []), - createModuleDescription('vs/workbench/services/search/node/worker/searchWorkerApp', []), + createModuleDescription('vs/workbench/services/search/node/legacy/worker/searchWorkerApp', []), createModuleDescription('vs/workbench/services/files/node/watcher/unix/watcherApp', []), createModuleDescription('vs/workbench/services/files/node/watcher/nsfw/watcherApp', []), diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index e43c1246438..4a99d73fc71 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -741,15 +741,15 @@ export abstract class BaseNavigationAction extends Action { return Promise.resolve(false); } - protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Thenable { return Promise.resolve(true); } - protected navigateOnPanelFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + protected navigateOnPanelFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Thenable { return Promise.resolve(true); } - protected navigateOnSidebarFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + protected navigateOnSidebarFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Thenable { return Promise.resolve(true); } @@ -763,7 +763,7 @@ export abstract class BaseNavigationAction extends Action { return this.panelService.openPanel(activePanelId, true); } - protected navigateToSidebar(): Promise { + protected navigateToSidebar(): Thenable { if (!this.partService.isVisible(Parts.SIDEBAR_PART)) { return Promise.resolve(false); } @@ -824,7 +824,7 @@ export class NavigateLeftAction extends BaseNavigationAction { }); } - protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { + protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Thenable { if (isPanelPositionDown && isSidebarPositionLeft) { return this.navigateToSidebar(); } @@ -880,7 +880,7 @@ export class NavigateRightAction extends BaseNavigationAction { }); } - protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Thenable { if (!isSidebarPositionLeft) { return this.navigateToSidebar(); } diff --git a/src/vs/workbench/parts/comments/common/commentModel.ts b/src/vs/workbench/parts/comments/common/commentModel.ts index 4c96d404f98..64d0efd2cfa 100644 --- a/src/vs/workbench/parts/comments/common/commentModel.ts +++ b/src/vs/workbench/parts/comments/common/commentModel.ts @@ -127,7 +127,7 @@ export class CommentsModel { } private groupByResource(commentThreads: CommentThread[]): ResourceWithCommentThreads[] { - const resourceCommentThreads = [] as ResourceWithCommentThreads[]; + const resourceCommentThreads: ResourceWithCommentThreads[] = []; const commentThreadsByResource = new Map(); for (const group of groupBy(commentThreads, CommentsModel._compareURIs)) { commentThreadsByResource.set(group[0].resource, new ResourceWithCommentThreads(URI.parse(group[0].resource), group)); diff --git a/src/vs/workbench/parts/comments/electron-browser/commentService.ts b/src/vs/workbench/parts/comments/electron-browser/commentService.ts index 3eece77e45c..e802d811794 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentService.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentService.ts @@ -135,7 +135,7 @@ export class CommentService extends Disposable implements ICommentService { } getComments(resource: URI): Promise { - const result = []; + const result: Promise[] = []; for (const handle of keys(this._commentProviders)) { const provider = this._commentProviders.get(handle); if ((provider).provideDocumentComments) { diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts index 8027233c499..dc21abb9fff 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts @@ -512,7 +512,7 @@ export class ReviewController implements IEditorContribution { }); }); - const commentingRanges = []; + const commentingRanges: IRange[] = []; this._commentInfos.forEach(info => { commentingRanges.push(...info.commentingRanges); }); diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts index 1b445b8126e..84e80059d73 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts @@ -49,7 +49,7 @@ export class CommentsPanel extends Panel { super(COMMENTS_PANEL_ID, telemetryService, themeService, storageService); } - public create(parent: HTMLElement): Promise { + public create(parent: HTMLElement): void { super.create(parent); dom.addClass(parent, 'comments-panel'); @@ -70,7 +70,7 @@ export class CommentsPanel extends Panel { this.applyStyles(styleElement); }); - return this.render(); + this.render(); } private applyStyles(styleElement: HTMLStyleElement) { diff --git a/src/vs/workbench/parts/debug/browser/breakpointsView.ts b/src/vs/workbench/parts/debug/browser/breakpointsView.ts index 3f470d812b4..3c648070600 100644 --- a/src/vs/workbench/parts/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/parts/debug/browser/breakpointsView.ts @@ -619,7 +619,7 @@ export function getBreakpointMessageAndClassName(debugService: IDebugService, br } if (breakpoint.logMessage || breakpoint.condition || breakpoint.hitCondition) { - const messages = []; + const messages: string[] = []; if (breakpoint.logMessage) { if (session && !session.capabilities.supportsLogPoints) { return { diff --git a/src/vs/workbench/parts/debug/browser/debugViewlet.ts b/src/vs/workbench/parts/debug/browser/debugViewlet.ts index e026eaa791a..dea2d392366 100644 --- a/src/vs/workbench/parts/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/parts/debug/browser/debugViewlet.ts @@ -64,10 +64,9 @@ export class DebugViewlet extends ViewContainerViewlet { })); } - create(parent: HTMLElement): Promise { - return super.create(parent).then(() => { - DOM.addClass(parent, 'debug-viewlet'); - }); + create(parent: HTMLElement): void { + super.create(parent); + DOM.addClass(parent, 'debug-viewlet'); } public focus(): void { diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 16249f43517..bbe682f7742 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -282,8 +282,8 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati // --- Creation - async create(parent: HTMLElement): Promise { - await super.create(parent); + create(parent: HTMLElement): void { + super.create(parent); this.container = dom.append(parent, $('.repl')); this.treeContainer = dom.append(this.container, $('.repl-tree')); this.createReplInput(this.container); diff --git a/src/vs/workbench/parts/experiments/electron-browser/experimentalPrompt.ts b/src/vs/workbench/parts/experiments/electron-browser/experimentalPrompt.ts index d92206737f5..f0fb773e435 100644 --- a/src/vs/workbench/parts/experiments/electron-browser/experimentalPrompt.ts +++ b/src/vs/workbench/parts/experiments/electron-browser/experimentalPrompt.ts @@ -10,6 +10,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionsViewlet } from 'vs/workbench/parts/extensions/common/extensions'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { language } from 'vs/base/common/platform'; export class ExperimentalPrompts extends Disposable implements IWorkbenchContribution { private _disposables: IDisposable[] = []; @@ -50,7 +51,8 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib }; const actionProperties = (experiment.action.properties); - if (!actionProperties || !actionProperties.promptText) { + const promptText = ExperimentalPrompts.getPromptText(actionProperties, language); + if (!actionProperties || !promptText) { return; } if (!actionProperties.commands) { @@ -80,7 +82,7 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib }; }); - this.notificationService.prompt(Severity.Info, actionProperties.promptText, choices, { + this.notificationService.prompt(Severity.Info, promptText, choices, { onCancel: () => { logTelemetry(); this.experimentService.markAsCompleted(experiment.id); @@ -91,4 +93,15 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib dispose() { this._disposables = dispose(this._disposables); } + + static getPromptText(actionProperties: IExperimentActionPromptProperties, displayLanguage: string): string { + if (typeof actionProperties.promptText === 'string') { + return actionProperties.promptText; + } + displayLanguage = displayLanguage.toLowerCase(); + if (!actionProperties.promptText[displayLanguage] && displayLanguage.indexOf('-') === 2) { + displayLanguage = displayLanguage.substr(0, 2); + } + return actionProperties.promptText[displayLanguage]; + } } diff --git a/src/vs/workbench/parts/experiments/node/experimentService.ts b/src/vs/workbench/parts/experiments/node/experimentService.ts index d407611aa03..a3002d70b51 100644 --- a/src/vs/workbench/parts/experiments/node/experimentService.ts +++ b/src/vs/workbench/parts/experiments/node/experimentService.ts @@ -23,6 +23,7 @@ import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/co import { WorkspaceStats } from 'vs/workbench/parts/stats/node/workspaceStats'; import { CancellationToken } from 'vs/base/common/cancellation'; import { distinct } from 'vs/base/common/arrays'; +import { lastSessionDateStorageKey } from 'vs/platform/telemetry/node/workbenchCommonProperties'; interface IExperimentStorageState { enabled: boolean; @@ -43,6 +44,7 @@ interface IRawExperiment { enabled?: boolean; condition?: { insidersOnly?: boolean; + newUser?: boolean; displayLanguage?: string; installedExtensions?: { excludes?: string[]; @@ -76,7 +78,7 @@ export enum ExperimentActionType { } export interface IExperimentActionPromptProperties { - promptText: string; + promptText: string | { [key: string]: string }; commands: IExperimentActionPromptCommand[]; } @@ -333,6 +335,12 @@ export class ExperimentService extends Disposable implements IExperimentService return TPromise.wrap(ExperimentState.NoRun); } + const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL); + if ((experiment.condition.newUser === true && !isNewUser) + || (experiment.condition.newUser === false && isNewUser)) { + return TPromise.wrap(ExperimentState.NoRun); + } + if (typeof experiment.condition.displayLanguage === 'string') { let localeToCheck = experiment.condition.displayLanguage.toLowerCase(); let displayLanguage = language.toLowerCase(); diff --git a/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts b/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts index 55472aabcf8..7db19c9eef7 100644 --- a/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts +++ b/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ExperimentService, ExperimentActionType, ExperimentState } from 'vs/workbench/parts/experiments/node/experimentService'; +import { ExperimentService, ExperimentActionType, ExperimentState, IExperiment } from 'vs/workbench/parts/experiments/node/experimentService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices'; @@ -26,6 +26,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { assign } from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { lastSessionDateStorageKey } from 'vs/platform/telemetry/node/workbenchCommonProperties'; let experimentData = { experiments: [] @@ -122,7 +123,7 @@ suite('Experiment Service', () => { }; testObject = instantiationService.createInstance(TestExperimentService); - const tests = []; + const tests: TPromise[] = []; tests.push(testObject.getExperimentById('experiment1')); tests.push(testObject.getExperimentById('experiment2')); tests.push(testObject.getExperimentById('experiment3')); @@ -173,6 +174,94 @@ suite('Experiment Service', () => { }); }); + test('NewUsers experiment shouldnt be enabled for old users', () => { + experimentData = { + experiments: [ + { + id: 'experiment1', + enabled: true, + condition: { + newUser: true + } + } + ] + }; + + instantiationService.stub(IStorageService, { + get: (a, b, c) => { + return a === lastSessionDateStorageKey ? 'some-date' : undefined; + }, + getBoolean: (a, b, c) => c, store: () => { }, remove: () => { } + }); + testObject = instantiationService.createInstance(TestExperimentService); + return testObject.getExperimentById('experiment1').then(result => { + assert.equal(result.enabled, true); + assert.equal(result.state, ExperimentState.NoRun); + }); + }); + + test('OldUsers experiment shouldnt be enabled for new users', () => { + experimentData = { + experiments: [ + { + id: 'experiment1', + enabled: true, + condition: { + newUser: false + } + } + ] + }; + + testObject = instantiationService.createInstance(TestExperimentService); + return testObject.getExperimentById('experiment1').then(result => { + assert.equal(result.enabled, true); + assert.equal(result.state, ExperimentState.NoRun); + }); + }); + + test('Experiment without NewUser condition should be enabled for old users', () => { + experimentData = { + experiments: [ + { + id: 'experiment1', + enabled: true, + condition: {} + } + ] + }; + + instantiationService.stub(IStorageService, { + get: (a, b, c) => { + return a === lastSessionDateStorageKey ? 'some-date' : undefined; + }, + getBoolean: (a, b, c) => c, store: () => { }, remove: () => { } + }); + testObject = instantiationService.createInstance(TestExperimentService); + return testObject.getExperimentById('experiment1').then(result => { + assert.equal(result.enabled, true); + assert.equal(result.state, ExperimentState.Run); + }); + }); + + test('Experiment without NewUser condition should be enabled for new users', () => { + experimentData = { + experiments: [ + { + id: 'experiment1', + enabled: true, + condition: {} + } + ] + }; + + testObject = instantiationService.createInstance(TestExperimentService); + return testObject.getExperimentById('experiment1').then(result => { + assert.equal(result.enabled, true); + assert.equal(result.state, ExperimentState.Run); + }); + }); + test('Experiment with no matching display language should be disabled', () => { experimentData = { experiments: [ @@ -546,7 +635,7 @@ suite('Experiment Service', () => { testObject = instantiationService.createInstance(TestExperimentService); - const tests = []; + const tests: TPromise[] = []; tests.push(testObject.getExperimentById('experiment1')); tests.push(testObject.getExperimentById('experiment2')); tests.push(testObject.getExperimentById('experiment3')); diff --git a/src/vs/workbench/parts/experiments/test/electron-browser/experimentalPrompts.test.ts b/src/vs/workbench/parts/experiments/test/electron-browser/experimentalPrompts.test.ts index 995f1498e45..44df807b708 100644 --- a/src/vs/workbench/parts/experiments/test/electron-browser/experimentalPrompts.test.ts +++ b/src/vs/workbench/parts/experiments/test/electron-browser/experimentalPrompts.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; -import { IExperiment, ExperimentActionType, IExperimentService, ExperimentState } from 'vs/workbench/parts/experiments/node/experimentService'; +import { IExperiment, ExperimentActionType, IExperimentService, ExperimentState, IExperimentActionPromptProperties } from 'vs/workbench/parts/experiments/node/experimentService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { Emitter } from 'vs/base/common/event'; @@ -157,4 +157,28 @@ suite('Experimental Prompts', () => { }); }); + + test('Test getPromptText', () => { + const simpleTextCase: IExperimentActionPromptProperties = { + promptText: 'My simple prompt', + commands: [] + }; + const englishTextCase: IExperimentActionPromptProperties = { + promptText: { + en: 'My simple prompt for en' + }, + commands: [] + }; + const englishUSTextCase: IExperimentActionPromptProperties = { + promptText: { + 'en-us': 'My simple prompt for en' + }, + commands: [] + }; + assert.equal(ExperimentalPrompts.getPromptText(simpleTextCase, 'any-language'), simpleTextCase.promptText); + assert.equal(ExperimentalPrompts.getPromptText(englishTextCase, 'en'), englishTextCase.promptText['en']); + assert.equal(ExperimentalPrompts.getPromptText(englishUSTextCase, 'en-us'), englishUSTextCase.promptText['en-us']); + assert.equal(ExperimentalPrompts.getPromptText(englishTextCase, 'en-au'), englishTextCase.promptText['en']); + assert.equal(!!ExperimentalPrompts.getPromptText(englishTextCase, 'de'), false); + }); }); \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 31ba8d61884..e3bc078e9ef 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -319,7 +319,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe const regEx = new RegExp(EXTENSION_IDENTIFIER_PATTERN); - const invalidExtensions = []; + const invalidExtensions: string[] = []; let message = ''; const regexFilter = (ids: string[]) => { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index eacb5a6bd2b..a67af30c64a 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -1044,7 +1044,12 @@ export class ReloadAction extends Action { .then(runningExtensions => this.computeReloadState(runningExtensions, installed)); }).then(() => { this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass; - this.label = this.useLongLabel ? this.tooltip : localize('reloadAction', "Reload"); + if (this.useLongLabel) { + this.label = this.tooltip; + this.tooltip = ''; + } else { + this.label = localize('reloadAction', "Reload"); + } }); } @@ -1129,7 +1134,7 @@ export class ShowEnabledExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1152,7 +1157,7 @@ export class ShowInstalledExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1175,7 +1180,7 @@ export class ShowDisabledExtensionsAction extends Action { super(id, label, 'null', true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1207,7 +1212,7 @@ export class ClearExtensionsInputAction extends Action { this.enabled = !!value; } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1234,7 +1239,7 @@ export class ShowBuiltInExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1257,7 +1262,7 @@ export class ShowOutdatedExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1280,7 +1285,7 @@ export class ShowPopularExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1303,7 +1308,7 @@ export class ShowRecommendedExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1336,7 +1341,7 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { this.recommendations = recommendations; } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1379,7 +1384,7 @@ export class InstallRecommendedExtensionAction extends Action { this.extensionId = extensionId; } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1475,7 +1480,7 @@ export class ShowRecommendedKeymapExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1498,7 +1503,7 @@ export class ShowLanguageExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1521,7 +1526,7 @@ export class ShowAzureExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1560,7 +1565,7 @@ export class ChangeSortAction extends Action { this.enabled = value && this.query.isValid() && !this.query.equals(query); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1598,7 +1603,7 @@ export class ChangeGroupAction extends Action { this.query = new Query(query.value, query.sortBy, this.groupBy || query.groupBy); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 65c54776600..a54f1666b13 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -325,7 +325,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio }, this, this.disposables); } - create(parent: HTMLElement): Promise { + create(parent: HTMLElement): void { addClass(parent, 'extensions-viewlet'); this.root = parent; @@ -358,7 +358,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.searchBox.onShouldFocusResults(() => this.focusListView(), this, this.disposables); this.extensionsBox = append(this.root, $('.extensions')); - return super.create(this.extensionsBox); + super.create(this.extensionsBox); } setVisible(visible: boolean): Promise { diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index fac339e0e34..9ca44f3c798 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -615,7 +615,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } private syncWithGallery(): Promise { - const ids = [], names = []; + const ids: string[] = [], names: string[] = []; for (const installed of this.installed) { if (installed.type === LocalExtensionType.User) { if (installed.uuid) { @@ -626,7 +626,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } } - const promises = []; + const promises: Promise>[] = []; if (ids.length) { promises.push(this.queryGallery({ ids, pageSize: ids.length })); } diff --git a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts index da2fb932175..c986aa7cea3 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts @@ -175,10 +175,9 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi this._register(this.contextService.onDidChangeWorkspaceName(e => this.updateTitleArea())); } - create(parent: HTMLElement): Promise { - return super.create(parent).then(() => { - DOM.addClass(parent, 'explorer-viewlet'); - }); + create(parent: HTMLElement): void { + super.create(parent); + DOM.addClass(parent, 'explorer-viewlet'); } protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPanel { diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index de875c725f0..099168e9c8d 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -329,7 +329,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView if (visible) { // If a refresh was requested and we are now visible, run it - let refreshPromise: Thenable = Promise.resolve(null); + let refreshPromise: Thenable = TPromise.as(null); if (this.shouldRefresh) { refreshPromise = this.doRefresh(); this.shouldRefresh = false; // Reset flag @@ -473,7 +473,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView public getOptimalWidth(): number { const parentNode = this.explorerViewer.getHTMLElement(); - const childNodes = [].slice.call(parentNode.querySelectorAll('.explorer-item .label-name')); // select all file labels + const childNodes = ([] as Element[]).slice.call(parentNode.querySelectorAll('.explorer-item .label-name')); // select all file labels return DOM.getLargestChildWidth(parentNode, childNodes); } diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index 1a9f0656dd7..52b766d177d 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -108,7 +108,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.setCurrentActiveEditor(); } - public create(parent: HTMLElement): Promise { + public create(parent: HTMLElement): void { super.create(parent); this.rangeHighlightDecorations = this._register(this.instantiationService.createInstance(RangeHighlightDecorations)); @@ -129,8 +129,6 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.onDidBlur(() => this.panelFoucusContextKey.set(false)); this.render(); - - return Promise.resolve(null); } public getTitle(): string { @@ -653,4 +651,4 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { super.dispose(); this.tree.dispose(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/markers/electron-browser/messages.ts b/src/vs/workbench/parts/markers/electron-browser/messages.ts index 50e2086b586..93cf9007d64 100644 --- a/src/vs/workbench/parts/markers/electron-browser/messages.ts +++ b/src/vs/workbench/parts/markers/electron-browser/messages.ts @@ -18,7 +18,6 @@ export default class Messages { public static PROBLEMS_PANEL_CONFIGURATION_AUTO_REVEAL: string = nls.localize('problems.panel.configuration.autoreveal', "Controls whether Problems view should automatically reveal files when opening them."); public static MARKERS_PANEL_TITLE_PROBLEMS: string = nls.localize('markers.panel.title.problems', "Problems"); - public static MARKERS_PANEL_ARIA_LABEL_PROBLEMS_TREE: string = nls.localize('markers.panel.aria.label.problems.tree', "Problems grouped by files"); public static MARKERS_PANEL_NO_PROBLEMS_BUILT: string = nls.localize('markers.panel.no.problems.build', "No problems have been detected in the workspace so far."); public static MARKERS_PANEL_NO_PROBLEMS_FILTERS: string = nls.localize('markers.panel.no.problems.filters', "No results found with provided filter criteria."); diff --git a/src/vs/workbench/parts/output/browser/outputActions.ts b/src/vs/workbench/parts/output/browser/outputActions.ts index 54abf9b78b9..287425bc6db 100644 --- a/src/vs/workbench/parts/output/browser/outputActions.ts +++ b/src/vs/workbench/parts/output/browser/outputActions.ts @@ -122,7 +122,7 @@ export class SwitchOutputActionItem extends SelectActionItem { @IThemeService themeService: IThemeService, @IContextViewService contextViewService: IContextViewService ) { - super(null, action, [], 0, contextViewService, { ariaLabel: nls.localize('outputs', 'Outputs') }); + super(null, action, [], 0, contextViewService, { ariaLabel: nls.localize('outputChannels', 'Output Channels.') }); let outputChannelRegistry = Registry.as(OutputExt.OutputChannels); this.toDispose.push(outputChannelRegistry.onDidRegisterChannel(() => this.updateOtions(this.outputService.getActiveChannel().id))); diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index 021e1fb525d..01b06015935 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -787,7 +787,7 @@ class ActionsColumn extends Column { render(keybindingItemEntry: IKeybindingItemEntry): void { this.actionBar.clear(); - const actions = []; + const actions: IAction[] = []; if (keybindingItemEntry.keybindingItem.keybinding) { actions.push(this.createEditAction(keybindingItemEntry)); } else { diff --git a/src/vs/workbench/parts/preferences/browser/settingsLayout.ts b/src/vs/workbench/parts/preferences/browser/settingsLayout.ts index e98adfcb7bb..2dc95fd5a3c 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsLayout.ts @@ -127,7 +127,7 @@ export const tocData: ITOCEntry = { { id: 'features/search', label: localize('search', "Search"), - settings: ['search.*'] + settings: ['search.*', 'searchRipgrep.*'] } , { diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 6de41e6defb..5224b1cd0a8 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -598,7 +598,7 @@ export class SettingsRenderer implements ITreeRenderer { private renderGroupTitleTemplate(container: HTMLElement): IGroupTitleTemplate { DOM.addClass(container, 'group-title'); - const toDispose = []; + const toDispose: IDisposable[] = []; const template: IGroupTitleTemplate = { parent: container, toDispose @@ -624,7 +624,7 @@ export class SettingsRenderer implements ITreeRenderer { const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); - const toDispose = []; + const toDispose: IDisposable[] = []; const toolbarContainer = DOM.append(container, $('.setting-toolbar-container')); const toolbar = this.renderSettingToolbar(toolbarContainer); @@ -775,7 +775,7 @@ export class SettingsRenderer implements ITreeRenderer { const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); - const toDispose = []; + const toDispose: IDisposable[] = []; const checkbox = new Checkbox({ actionClassName: 'setting-value-checkbox', isChecked: true, title: '', inputActiveOptionBorder: null }); controlElement.appendChild(checkbox.domNode); toDispose.push(checkbox); @@ -966,7 +966,7 @@ export class SettingsRenderer implements ITreeRenderer { } private renderNewExtensionsTemplate(container: HTMLElement): ISettingNewExtensionsTemplate { - const toDispose = []; + const toDispose: IDisposable[] = []; container.classList.add('setting-item-new-extensions'); diff --git a/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts index a449db5aa11..c48c4e88020 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts @@ -137,7 +137,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { const { isConfigured, inspected, targetSelector } = inspectResult; const displayValue = isConfigured ? inspected[targetSelector] : inspected.default; - const overriddenScopeList = []; + const overriddenScopeList: string[] = []; if (targetSelector === 'user' && typeof inspected.workspace !== 'undefined') { overriddenScopeList.push(localize('workspace', "Workspace")); } @@ -281,7 +281,7 @@ export class SettingsTreeModel { element.parent = parent; element.level = this.getDepth(element); - const children = []; + const children: SettingsTreeGroupChild[] = []; if (tocEntry.settings) { const settingChildren = tocEntry.settings.map(s => this.createSettingsTreeSettingElement(s, element)) .filter(el => el.setting.deprecationMessage ? el.isConfigured : true); diff --git a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css index 05855b41acd..7f7bed3a256 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css @@ -450,7 +450,6 @@ border-radius: 3px; margin-right: 9px; margin-left: 0px; - margin-top: 4px; padding: 0px; background-size: 16px !important; } diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index a5abd5e68ad..504ebc980b8 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -813,29 +813,6 @@ export class SettingsEditor2 extends BaseEditor { } */ this.telemetryService.publicLog('settingsEditor.settingModified', data); - - const data2 = { - key: props.key, - groupId, - nlpIndex, - displayIndex, - showConfiguredOnly: props.showConfiguredOnly, - isReset: props.isReset, - target: reportedTarget - }; - - /* __GDPR__ - "settingsEditor.settingModified" : { - "key" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "groupId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "nlpIndex" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "displayIndex" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "showConfiguredOnly" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "isReset" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "target" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('settingsEditor.settingModified2', data2); } private render(token: CancellationToken): TPromise { @@ -898,7 +875,7 @@ export class SettingsEditor2 extends BaseEditor { // Warn for settings not included in layout if (settingsResult.leftoverSettings.size && !this.hasWarnedMissingSettings) { - const settingKeyList = []; + const settingKeyList: string[] = []; settingsResult.leftoverSettings.forEach(s => { settingKeyList.push(s.key); }); @@ -1147,22 +1124,6 @@ export class SettingsEditor2 extends BaseEditor { } */ this.telemetryService.publicLog('settingsEditor.filter', data); - - const data2 = { - durations, - counts, - requestCount - }; - - /* __GDPR__ - "settingsEditor.filter" : { - "durations.nlpResult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "counts.nlpResult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "counts.filterResult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "requestCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('settingsEditor.filter2', data2); } private triggerFilterPreferences(query: string): TPromise { diff --git a/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts b/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts index 16596d153e0..24d959b54ec 100644 --- a/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts @@ -170,7 +170,7 @@ export class ViewPickerHandler extends QuickOpenHandler { const channels = this.outputService.getChannelDescriptors(); channels.forEach((channel, index) => { const outputCategory = nls.localize('channels', "Output"); - const entry = new ViewEntry(channel.label, outputCategory, () => this.outputService.showChannel(channel.id)); + const entry = new ViewEntry(channel.log ? nls.localize('logChannel', "Log ({0})", channel.label) : channel.label, outputCategory, () => this.outputService.showChannel(channel.id)); viewEntries.push(entry); }); diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 7f1782b2979..d2329cf192e 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -1067,37 +1067,36 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle this.disposables.push(this.contributedViews); } - create(parent: HTMLElement): Promise { - return super.create(parent).then(() => { - this.el = parent; - addClass(this.el, 'scm-viewlet'); - addClass(this.el, 'empty'); - append(parent, $('div.empty-message', null, localize('no open repo', "No source control providers registered."))); + create(parent: HTMLElement): void { + super.create(parent); + this.el = parent; + addClass(this.el, 'scm-viewlet'); + addClass(this.el, 'empty'); + append(parent, $('div.empty-message', null, localize('no open repo', "No source control providers registered."))); - this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); - this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); - this.scmService.repositories.forEach(r => this.onDidAddRepository(r)); + this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); + this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); + this.scmService.repositories.forEach(r => this.onDidAddRepository(r)); - const onDidUpdateConfiguration = filterEvent(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.alwaysShowProviders')); - onDidUpdateConfiguration(this.onDidChangeRepositories, this, this.disposables); + const onDidUpdateConfiguration = filterEvent(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.alwaysShowProviders')); + onDidUpdateConfiguration(this.onDidChangeRepositories, this, this.disposables); - this.onDidChangeRepositories(); + this.onDidChangeRepositories(); - this.contributedViews.onDidAdd(this.onDidAddContributedViews, this, this.disposables); - this.contributedViews.onDidRemove(this.onDidRemoveContributedViews, this, this.disposables); + this.contributedViews.onDidAdd(this.onDidAddContributedViews, this, this.disposables); + this.contributedViews.onDidRemove(this.onDidRemoveContributedViews, this, this.disposables); - let index = this.getContributedViewsStartIndex(); - const contributedViews: IAddedViewDescriptorRef[] = this.contributedViews.visibleViewDescriptors.map(viewDescriptor => { - const size = this.contributedViews.getSize(viewDescriptor.id); - const collapsed = this.contributedViews.isCollapsed(viewDescriptor.id); - return { viewDescriptor, index: index++, size, collapsed }; - }); - if (contributedViews.length) { - this.onDidAddContributedViews(contributedViews); - } - - this.onDidSashChange(this.saveContributedViewSizes, this, this.disposables); + let index = this.getContributedViewsStartIndex(); + const contributedViews: IAddedViewDescriptorRef[] = this.contributedViews.visibleViewDescriptors.map(viewDescriptor => { + const size = this.contributedViews.getSize(viewDescriptor.id); + const collapsed = this.contributedViews.isCollapsed(viewDescriptor.id); + return { viewDescriptor, index: index++, size, collapsed }; }); + if (contributedViews.length) { + this.onDidAddContributedViews(contributedViews); + } + + this.onDidSashChange(this.saveContributedViewSizes, this, this.disposables); } private onDidAddRepository(repository: ISCMRepository): void { diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts index a7c98574b4c..757fe864db8 100644 --- a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -121,7 +121,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { } // Combine results. - const mergedResults = [].concat(...results.map(r => r.entries)); + const mergedResults: QuickOpenEntry[] = [].concat(...results.map(r => r.entries)); // Sort const compare = (elementA: QuickOpenEntry, elementB: QuickOpenEntry) => compareItemsByScore(elementA, elementB, query, true, QuickOpenItemAccessor, this.scorerCache); diff --git a/src/vs/workbench/parts/search/browser/openFileHandler.ts b/src/vs/workbench/parts/search/browser/openFileHandler.ts index 408b144a5e1..ec191af0f51 100644 --- a/src/vs/workbench/parts/search/browser/openFileHandler.ts +++ b/src/vs/workbench/parts/search/browser/openFileHandler.ts @@ -19,12 +19,12 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenHandler, EditorQuickOpenEntry } from 'vs/workbench/browser/quickopen'; -import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; +import { QueryBuilder, IFileQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IQueryOptions, ISearchService, IFileSearchStats, ISearchQuery, ISearchComplete } from 'vs/platform/search/common/search'; +import { ISearchService, IFileSearchStats, IFileQuery, ISearchComplete } from 'vs/platform/search/common/search'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IRange } from 'vs/editor/common/core/range'; @@ -171,7 +171,7 @@ export class OpenFileHandler extends QuickOpenHandler { return TPromise.wrap({ results: [{ resource: result }] }); } - return this.searchService.search(this.queryBuilder.file(this.contextService.getWorkspace().folders.map(folder => folder.uri), queryOptions), token); + return this.searchService.fileSearch(this.queryBuilder.file(this.contextService.getWorkspace().folders.map(folder => folder.uri), queryOptions), token); }).then(complete => { const results: QuickOpenEntry[] = []; @@ -200,8 +200,9 @@ export class OpenFileHandler extends QuickOpenHandler { return TPromise.as(null); } - private doResolveQueryOptions(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number): IQueryOptions { - const queryOptions: IQueryOptions = { + private doResolveQueryOptions(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number): IFileQueryBuilderOptions { + const queryOptions: IFileQueryBuilderOptions = { + _reason: 'openFileHandler', extraFileResources: getOutOfWorkspaceEditorResources(this.editorService, this.contextService), filePattern: query.value, cacheKey @@ -220,12 +221,13 @@ export class OpenFileHandler extends QuickOpenHandler { } onOpen(): void { - this.cacheState = new CacheState(cacheKey => this.cacheQuery(cacheKey), query => this.searchService.search(query), cacheKey => this.searchService.clearCache(cacheKey), this.cacheState); + this.cacheState = new CacheState(cacheKey => this.cacheQuery(cacheKey), query => this.searchService.fileSearch(query), cacheKey => this.searchService.clearCache(cacheKey), this.cacheState); this.cacheState.load(); } - private cacheQuery(cacheKey: string): ISearchQuery { - const options: IQueryOptions = { + private cacheQuery(cacheKey: string): IFileQuery { + const options: IFileQueryBuilderOptions = { + _reason: 'openFileHandler', extraFileResources: getOutOfWorkspaceEditorResources(this.editorService, this.contextService), filePattern: '', cacheKey: cacheKey, @@ -268,12 +270,12 @@ enum LoadingPhase { export class CacheState { private _cacheKey = defaultGenerator.nextId(); - private query: ISearchQuery; + private query: IFileQuery; private loadingPhase = LoadingPhase.Created; private promise: TPromise; - constructor(cacheQuery: (cacheKey: string) => ISearchQuery, private doLoad: (query: ISearchQuery) => TPromise, private doDispose: (cacheKey: string) => TPromise, private previous: CacheState) { + constructor(cacheQuery: (cacheKey: string) => IFileQuery, private doLoad: (query: IFileQuery) => TPromise, private doDispose: (cacheKey: string) => TPromise, private previous: CacheState) { this.query = cacheQuery(this._cacheKey); if (this.previous) { const current = objects.assign({}, this.query, { cacheKey: null }); diff --git a/src/vs/workbench/parts/search/browser/patternInputWidget.ts b/src/vs/workbench/parts/search/browser/patternInputWidget.ts index ced2a9bbba2..354d942ac0b 100644 --- a/src/vs/workbench/parts/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/parts/search/browser/patternInputWidget.ts @@ -31,7 +31,6 @@ export class PatternInputWidget extends Widget { public inputFocusTracker: dom.IFocusTracker; - protected onOptionChange: (event: Event) => void; private width: number; private placeholder: string; private ariaLabel: string; @@ -50,7 +49,6 @@ export class PatternInputWidget extends Widget { @IContextKeyService private contextKeyService: IContextKeyService ) { super(); - this.onOptionChange = null; this.width = options.width || 100; this.placeholder = options.placeholder || ''; this.ariaLabel = options.ariaLabel || nls.localize('defaultLabel', "input"); @@ -70,19 +68,6 @@ export class PatternInputWidget extends Widget { } } - public on(eventType: string, handler: (event: Event) => void): PatternInputWidget { - switch (eventType) { - case 'keydown': - case 'keyup': - this._register(dom.addDisposableListener(this.inputBox.inputElement, eventType, handler)); - break; - case PatternInputWidget.OPTION_CHANGE: - this.onOptionChange = handler; - break; - } - return this; - } - public setWidth(newWidth: number): void { this.width = newWidth; this.domNode.style.width = this.width + 'px'; @@ -218,7 +203,6 @@ export class ExcludePatternInputWidget extends PatternInputWidget { isChecked: true, })); this._register(this.useExcludesAndIgnoreFilesBox.onChange(viaKeyboard => { - this.onOptionChange(null); if (!viaKeyboard) { this.inputBox.focus(); } diff --git a/src/vs/workbench/parts/search/browser/replaceService.ts b/src/vs/workbench/parts/search/browser/replaceService.ts index 92aa7ab0116..54ff7977da3 100644 --- a/src/vs/workbench/parts/search/browser/replaceService.ts +++ b/src/vs/workbench/parts/search/browser/replaceService.ts @@ -18,7 +18,7 @@ import { IProgressRunner } from 'vs/platform/progress/common/progress'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ScrollType } from 'vs/editor/common/editorCommon'; -import { ITextModel } from 'vs/editor/common/model'; +import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ResourceTextEdit } from 'vs/editor/common/modes'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; @@ -164,7 +164,7 @@ export class ReplaceService implements IReplaceService { private applyEditsToPreview(fileMatch: FileMatch, replaceModel: ITextModel): void { const resourceEdits = this.createEdits(fileMatch, replaceModel.uri); - const modelEdits = []; + const modelEdits: IIdentifiedSingleEditOperation[] = []; for (const resourceEdit of resourceEdits) { for (const edit of resourceEdit.edits) { const range = Range.lift(edit.range); diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index a00c799ff6b..02c5c152234 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -33,7 +33,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { TreeResourceNavigator, WorkbenchTree } from 'vs/platform/list/browser/listService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IPatternInfo, IQueryOptions, ISearchComplete, ISearchConfiguration, ISearchHistoryService, ISearchProgressItem, ISearchQuery, VIEW_ID, ISearchHistoryValues } from 'vs/platform/search/common/search'; +import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchHistoryService, ISearchProgressItem, VIEW_ID, ISearchHistoryValues, ITextQuery } from 'vs/platform/search/common/search'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, editorFindMatchHighlight, editorFindMatchHighlightBorder, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -49,7 +49,7 @@ import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLe import { SearchAccessibilityProvider, SearchDataSource, SearchFilter, SearchRenderer, SearchSorter, SearchTreeController } from 'vs/workbench/parts/search/browser/searchResultsView'; import { ISearchWidgetOptions, SearchWidget } from 'vs/workbench/parts/search/browser/searchWidget'; import * as Constants from 'vs/workbench/parts/search/common/constants'; -import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; +import { QueryBuilder, ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; import { getOutOfWorkspaceEditorResources } from 'vs/workbench/parts/search/common/search'; import { FileMatch, FileMatchOrMatch, FolderMatch, IChangeEvent, ISearchWorkbenchService, Match, SearchModel } from 'vs/workbench/parts/search/common/searchModel'; @@ -169,7 +169,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } } - public create(parent: HTMLElement): Promise { + public create(parent: HTMLElement): void { super.create(parent); this.viewModel = this._register(this.searchWorkbenchService.searchModel); @@ -273,8 +273,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this._register(this.onDidFocus(() => this.viewletFocused.set(true))); this._register(this.onDidBlur(() => this.viewletFocused.set(false))); - - return Promise.resolve(null); } public get searchAndReplaceWidget(): SearchWidget { @@ -1054,7 +1052,8 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { const charsPerLine = content.isRegExp ? 10000 : 250; - const options: IQueryOptions = { + const options: ITextQueryBuilderOptions = { + _reason: 'searchView', extraFileResources: getOutOfWorkspaceEditorResources(this.editorService, this.contextService), maxResults: SearchView.MAX_TEXT_RESULTS, disregardIgnoreFiles: !useExcludesAndIgnoreFiles, @@ -1073,7 +1072,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.viewModel.searchResult.clear(); }; - let query: ISearchQuery; + let query: ITextQuery; try { query = this.queryBuilder.text(content, folderResources.map(folder => folder.uri), options); } catch (err) { @@ -1090,7 +1089,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { }, onQueryValidationError); } - private validateQuery(query: ISearchQuery): TPromise { + private validateQuery(query: ITextQuery): TPromise { // Validate folderQueries const folderQueriesExistP = query.folderQueries.map(fq => { @@ -1112,7 +1111,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { }); } - private onQueryTriggered(query: ISearchQuery, excludePatternText: string, includePatternText: string): void { + private onQueryTriggered(query: ITextQuery, excludePatternText: string, includePatternText: string): void { this.inputPatternExcludes.onSearchSubmit(); this.inputPatternIncludes.onSearchSubmit(); @@ -1217,25 +1216,14 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { })); } else { const openSettingsLink = dom.append(p, $('a.pointer.prominent', { tabindex: 0 }, nls.localize('openSettings.message', "Open Settings"))); - this.messageDisposables.push(dom.addDisposableListener(openSettingsLink, dom.EventType.CLICK, (e: MouseEvent) => { - dom.EventHelper.stop(e, false); - - const options: ISettingsEditorOptions = { query: '.exclude' }; - this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? - this.preferencesService.openWorkspaceSettings(undefined, options) : - this.preferencesService.openGlobalSettings(undefined, options); - })); + this.addClickEvents(openSettingsLink, this.onOpenSettings); } if (completed) { dom.append(p, $('span', undefined, ' - ')); const learnMoreLink = dom.append(p, $('a.pointer.prominent', { tabindex: 0 }, nls.localize('openSettings.learnMore', "Learn More"))); - this.messageDisposables.push(dom.addDisposableListener(learnMoreLink, dom.EventType.CLICK, (e: MouseEvent) => { - dom.EventHelper.stop(e, false); - - window.open('https://go.microsoft.com/fwlink/?linkid=853977'); - })); + this.addClickEvents(learnMoreLink, this.onLearnMore); } if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { @@ -1323,6 +1311,40 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.viewModel.search(query, onProgress).then(onComplete, onError); } + private addClickEvents = (element: HTMLElement, handler: (event: any) => void): void => { + this.messageDisposables.push(dom.addDisposableListener(element, dom.EventType.CLICK, handler)); + this.messageDisposables.push(dom.addDisposableListener(element, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + let event = new StandardKeyboardEvent(e as KeyboardEvent); + let eventHandled = true; + + if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { + handler(e); + } else { + eventHandled = false; + } + + if (eventHandled) { + event.preventDefault(); + event.stopPropagation(); + } + })); + } + + private onOpenSettings = (e: dom.EventLike): void => { + dom.EventHelper.stop(e, false); + + const options: ISettingsEditorOptions = { query: '.exclude' }; + this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? + this.preferencesService.openWorkspaceSettings(undefined, options) : + this.preferencesService.openGlobalSettings(undefined, options); + } + + private onLearnMore = (e: MouseEvent): void => { + dom.EventHelper.stop(e, false); + + window.open('https://go.microsoft.com/fwlink/?linkid=853977'); + } + private updateSearchResultCount(): void { const fileCount = this.viewModel.searchResult.fileCount(); this.hasSearchResultsKey.set(fileCount > 0); diff --git a/src/vs/workbench/parts/search/browser/searchWidget.ts b/src/vs/workbench/parts/search/browser/searchWidget.ts index b56f1a72fae..e038970faf3 100644 --- a/src/vs/workbench/parts/search/browser/searchWidget.ts +++ b/src/vs/workbench/parts/search/browser/searchWidget.ts @@ -307,7 +307,7 @@ export class SearchWidget extends Widget { this._register(this.searchInputFocusTracker.onDidFocus(() => { this.searchInputBoxFocused.set(true); - const useGlobalFindBuffer = this.configurationService.getValue('search').globalFindClipboard; + const useGlobalFindBuffer = this.searchConfiguration.globalFindClipboard; if (!this.ignoreGlobalFindBufferOnNextFocus && useGlobalFindBuffer) { const globalBufferText = this.clipboardServce.readFindText(); if (this.previousGlobalFindBufferValue !== globalBufferText) { @@ -398,7 +398,9 @@ export class SearchWidget extends Widget { } if (strings.regExpContainsBackreference(value)) { - return { content: nls.localize('regexp.backreferenceValidationFailure', "Backreferences are not supported") }; + if (!this.searchConfiguration.usePCRE2) { + return { content: nls.localize('regexp.backreferenceValidationFailure', "Backreferences are not supported") }; + } } return null; @@ -475,8 +477,13 @@ export class SearchWidget extends Widget { } private submitSearch(): void { + this.searchInput.validate(); + if (!this.searchInput.inputBox.isInputValid()) { + return; + } + const value = this.searchInput.getValue(); - const useGlobalFindBuffer = this.configurationService.getValue('search').globalFindClipboard; + const useGlobalFindBuffer = this.searchConfiguration.globalFindClipboard; if (value) { if (useGlobalFindBuffer) { this.clipboardServce.writeFindText(value); @@ -492,6 +499,10 @@ export class SearchWidget extends Widget { this.replaceActionBar = null; super.dispose(); } + + private get searchConfiguration(): ISearchConfigurationProperties { + return this.configurationService.getValue('search'); + } } export function registerContributions() { diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index e0b4b36eb17..cabdf13f113 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -3,21 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; import * as arrays from 'vs/base/common/arrays'; -import * as objects from 'vs/base/common/objects'; import * as collections from 'vs/base/common/collections'; -import * as strings from 'vs/base/common/strings'; import * as glob from 'vs/base/common/glob'; +import { untildify } from 'vs/base/common/labels'; +import * as objects from 'vs/base/common/objects'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; +import * as strings from 'vs/base/common/strings'; import { URI as uri } from 'vs/base/common/uri'; -import { untildify } from 'vs/base/common/labels'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IPatternInfo, IQueryOptions, IFolderQuery, ISearchQuery, QueryType, ISearchConfiguration, getExcludes, pathIncludedInQuery } from 'vs/platform/search/common/search'; +import { isMultilineRegexSource } from 'vs/editor/common/model/textModelSearch'; +import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { isMultilineRegexSource } from 'vs/editor/common/model/textModelSearch'; +import { getExcludes, ICommonQueryProps, IFileQuery, IFolderQuery, IPatternInfo, ISearchConfiguration, ITextQuery, ITextSearchPreviewOptions, pathIncludedInQuery, QueryType } from 'vs/platform/search/common/search'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; export interface ISearchPathPattern { searchPath: uri; @@ -29,6 +29,33 @@ export interface ISearchPathsResult { pattern?: glob.IExpression; } +export interface ICommonQueryBuilderOptions { + _reason?: string; + excludePattern?: string; + includePattern?: string; + extraFileResources?: uri[]; + + maxResults?: number; + useRipgrep?: boolean; + disregardIgnoreFiles?: boolean; + disregardGlobalIgnoreFiles?: boolean; + disregardExcludeSettings?: boolean; + ignoreSymlinks?: boolean; +} + +export interface IFileQueryBuilderOptions extends ICommonQueryBuilderOptions { + filePattern?: string; + exists?: boolean; + sortByScore?: boolean; + cacheKey?: string; +} + +export interface ITextQueryBuilderOptions extends ICommonQueryBuilderOptions { + previewOptions?: ITextSearchPreviewOptions; + fileEncoding?: string; + maxFileSize?: number; +} + export class QueryBuilder { constructor( @@ -37,81 +64,78 @@ export class QueryBuilder { @IEnvironmentService private environmentService: IEnvironmentService ) { } - public text(contentPattern: IPatternInfo, folderResources?: uri[], options?: IQueryOptions): ISearchQuery { - return this.query(QueryType.Text, contentPattern, folderResources, options); + text(contentPattern: IPatternInfo, folderResources?: uri[], options?: ITextQueryBuilderOptions): ITextQuery { + contentPattern.isCaseSensitive = this.isCaseSensitive(contentPattern); + contentPattern.isMultiline = this.isMultiline(contentPattern); + const searchConfig = this.configurationService.getValue(); + contentPattern.wordSeparators = searchConfig.editor.wordSeparators; + + const fallbackToPCRE = folderResources && folderResources.some(folder => { + const folderConfig = this.configurationService.getValue({ resource: folder }); + return !folderConfig.search.useRipgrep; + }); + + const commonQuery = this.commonQuery(folderResources, options); + return { + ...commonQuery, + type: QueryType.Text, + contentPattern, + previewOptions: options && options.previewOptions, + maxFileSize: options && options.maxFileSize, + usePCRE2: searchConfig.search.usePCRE2 || fallbackToPCRE || false + }; } - public file(folderResources?: uri[], options?: IQueryOptions): ISearchQuery { - return this.query(QueryType.File, null, folderResources, options); + file(folderResources?: uri[], options?: IFileQueryBuilderOptions): IFileQuery { + const commonQuery = this.commonQuery(folderResources, options); + return { + ...commonQuery, + type: QueryType.File, + filePattern: options.filePattern + ? options.filePattern.trim() + : options.filePattern, + exists: options.exists, + sortByScore: options.sortByScore, + cacheKey: options.cacheKey + }; } - private query(type: QueryType, contentPattern?: IPatternInfo, folderResources?: uri[], options: IQueryOptions = {}): ISearchQuery { + private commonQuery(folderResources?: uri[], options: ICommonQueryBuilderOptions = {}): ICommonQueryProps { let { searchPaths, pattern: includePattern } = this.parseSearchPaths(options.includePattern); let excludePattern = this.parseExcludePattern(options.excludePattern); // Build folderQueries from searchPaths, if given, otherwise folderResources - let folderQueries = folderResources && folderResources.map(uri => this.getFolderQueryForRoot(uri, type === QueryType.File, options)); + let folderQueries = folderResources && folderResources.map(uri => this.getFolderQueryForRoot(uri, options)); if (searchPaths && searchPaths.length) { const allRootExcludes = folderQueries && this.mergeExcludesFromFolderQueries(folderQueries); - folderQueries = searchPaths.map(searchPath => this.getFolderQueryForSearchPath(searchPath)); + folderQueries = searchPaths.map(searchPath => this.getFolderQueryForSearchPath(searchPath)); // TODO Rob if (allRootExcludes) { excludePattern = objects.mixin(excludePattern || Object.create(null), allRootExcludes); } } - // TODO@rob - see #37998 - const useIgnoreFiles = !folderResources || folderResources.every(folder => { - const folderConfig = this.configurationService.getValue({ resource: folder }); - return folderConfig.search.useIgnoreFiles; - }); - - const useGlobalIgnoreFiles = !folderResources || folderResources.every(folder => { - const folderConfig = this.configurationService.getValue({ resource: folder }); - return folderConfig.search.useGlobalIgnoreFiles; - }); - const useRipgrep = !folderResources || folderResources.every(folder => { const folderConfig = this.configurationService.getValue({ resource: folder }); - return folderConfig.search.useRipgrep; + return !folderConfig.search.disableRipgrep; }); - const ignoreSymlinks = !this.configurationService.getValue().search.followSymlinks; - - if (contentPattern) { - contentPattern.isCaseSensitive = this.isCaseSensitive(contentPattern); - contentPattern.isMultiline = this.isMultiline(contentPattern); - - contentPattern.wordSeparators = this.configurationService.getValue().editor.wordSeparators; - } - - const query: ISearchQuery = { - type, + const queryProps: ICommonQueryProps = { + _reason: options._reason, folderQueries: folderQueries || [], usingSearchPaths: !!(searchPaths && searchPaths.length), extraFileResources: options.extraFileResources, - filePattern: options.filePattern - ? options.filePattern.trim() - : options.filePattern, + excludePattern, includePattern, maxResults: options.maxResults, - sortByScore: options.sortByScore, - cacheKey: options.cacheKey, - contentPattern, - useRipgrep, - disregardIgnoreFiles: options.disregardIgnoreFiles || !useIgnoreFiles, - disregardGlobalIgnoreFiles: options.disregardGlobalIgnoreFiles || !useGlobalIgnoreFiles, - disregardExcludeSettings: options.disregardExcludeSettings, - ignoreSymlinks, - previewOptions: options.previewOptions, - exists: options.exists + useRipgrep }; // Filter extraFileResources against global include/exclude patterns - they are already expected to not belong to a workspace - let extraFileResources = options.extraFileResources && options.extraFileResources.filter(extraFile => pathIncludedInQuery(query, extraFile.fsPath)); - query.extraFileResources = extraFileResources && extraFileResources.length ? extraFileResources : undefined; + let extraFileResources = options.extraFileResources && options.extraFileResources.filter(extraFile => pathIncludedInQuery(queryProps, extraFile.fsPath)); + queryProps.extraFileResources = extraFileResources && extraFileResources.length ? extraFileResources : undefined; - return query; + return queryProps; } /** @@ -235,7 +259,7 @@ export class QueryBuilder { }, Object.create(null)); } - private getExcludesForFolder(folderConfig: ISearchConfiguration, options: IQueryOptions): glob.IExpression | undefined { + private getExcludesForFolder(folderConfig: ISearchConfiguration, options: ICommonQueryBuilderOptions): glob.IExpression | undefined { return options.disregardExcludeSettings ? undefined : getExcludes(folderConfig); @@ -313,14 +337,15 @@ export class QueryBuilder { }; } - private getFolderQueryForRoot(folder: uri, perFolderUseIgnoreFiles: boolean, options?: IQueryOptions): IFolderQuery { + private getFolderQueryForRoot(folder: uri, options: ICommonQueryBuilderOptions): IFolderQuery { const folderConfig = this.configurationService.getValue({ resource: folder }); return { folder, excludePattern: this.getExcludesForFolder(folderConfig, options), fileEncoding: folderConfig.files && folderConfig.files.encoding, - disregardIgnoreFiles: perFolderUseIgnoreFiles ? !folderConfig.search.useIgnoreFiles : undefined, - disregardGlobalIgnoreFiles: perFolderUseIgnoreFiles ? !folderConfig.search.useGlobalIgnoreFiles : undefined + disregardIgnoreFiles: typeof options.disregardIgnoreFiles === 'boolean' ? options.disregardIgnoreFiles : !folderConfig.search.useIgnoreFiles, + disregardGlobalIgnoreFiles: typeof options.disregardGlobalIgnoreFiles === 'boolean' ? options.disregardGlobalIgnoreFiles : !folderConfig.search.useGlobalIgnoreFiles, + ignoreSymlinks: typeof options.ignoreSymlinks === 'boolean' ? options.ignoreSymlinks : !folderConfig.search.followSymlinks, }; } } diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index 095a46e2f52..2aa6b653660 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -21,7 +21,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProgressRunner } from 'vs/platform/progress/common/progress'; import { ReplacePattern } from 'vs/platform/search/common/replace'; -import { IFileMatch, IPatternInfo, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, TextSearchResult } from 'vs/platform/search/common/search'; +import { IFileMatch, IPatternInfo, ISearchComplete, ISearchProgressItem, ISearchService, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, TextSearchResult, ITextQuery } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; @@ -366,7 +366,7 @@ export class FolderMatch extends Disposable { private _unDisposedFileMatches: ResourceMap; private _replacingAll: boolean = false; - constructor(private _resource: URI | null, private _id: string, private _index: number, private _query: ISearchQuery, private _parent: SearchResult, private _searchModel: SearchModel, @IReplaceService private replaceService: IReplaceService, + constructor(private _resource: URI | null, private _id: string, private _index: number, private _query: ITextQuery, private _parent: SearchResult, private _searchModel: SearchModel, @IReplaceService private replaceService: IReplaceService, @IInstantiationService private instantiationService: IInstantiationService) { super(); this._fileMatches = new ResourceMap(); @@ -563,7 +563,7 @@ export class SearchResult extends Disposable { this._rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); } - public set query(query: ISearchQuery) { + public set query(query: ITextQuery) { // When updating the query we could change the roots, so ensure we clean up the old roots first. this.clear(); this._folderMatches = (query.folderQueries || []) @@ -574,7 +574,7 @@ export class SearchResult extends Disposable { this._otherFilesMatch = this.createFolderMatch(null, 'otherFiles', this._folderMatches.length + 1, query); } - private createFolderMatch(resource: URI | null, id: string, index: number, query: ISearchQuery): FolderMatch { + private createFolderMatch(resource: URI | null, id: string, index: number, query: ITextQuery): FolderMatch { const folderMatch = this.instantiationService.createInstance(FolderMatch, resource, id, index, query, this, this._searchModel); const disposable = folderMatch.onChange((event) => this._onChange.fire(event)); folderMatch.onDispose(() => disposable.dispose()); @@ -744,7 +744,7 @@ export class SearchResult extends Disposable { export class SearchModel extends Disposable { private _searchResult: SearchResult; - private _searchQuery: ISearchQuery | null = null; + private _searchQuery: ITextQuery | null = null; private _replaceActive: boolean = false; private _replaceString: string | null = null; private _replacePattern: ReplacePattern | null = null; @@ -787,7 +787,7 @@ export class SearchModel extends Disposable { return this._searchResult; } - public search(query: ISearchQuery, onProgress?: (result: ISearchProgressItem) => void): TPromise { + public search(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): TPromise { this.cancelSearch(); this._searchQuery = query; @@ -798,7 +798,7 @@ export class SearchModel extends Disposable { this._replacePattern = new ReplacePattern(this._replaceString, this._searchQuery.contentPattern); const tokenSource = this.currentCancelTokenSource = new CancellationTokenSource(); - const currentRequest = this.searchService.search(this._searchQuery, this.currentCancelTokenSource.token, p => { + const currentRequest = this.searchService.textSearch(this._searchQuery, this.currentCancelTokenSource.token, p => { progressEmitter.fire(); this.onSearchProgress(p); diff --git a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts index 62af8ecc289..a89f1ecfc9d 100644 --- a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts @@ -586,9 +586,16 @@ configurationRegistry.registerConfiguration({ }, 'search.useRipgrep': { type: 'boolean', - description: nls.localize('useRipgrep', "Controls whether to use ripgrep in text and file search."), + description: nls.localize('useRipgrep', "Deprecated. This setting now falls back on \"search.usePCRE2\"."), + deprecationMessage: nls.localize('useRipgrepDeprecated', "Deprecated. Consider \"search.usePCRE2\" for advanced regex feature support."), default: true }, + 'search.disableRipgrep': { + type: 'boolean', + description: nls.localize('disableRipgrep', "Deprecated. Controls whether to use ripgrep in text and file search."), + deprecationMessage: nls.localize('disableRipgrepDeprecated', "Deprecated. Consider \"search.usePCRE2\" for advanced regex feature support."), + default: false + }, 'search.useIgnoreFiles': { type: 'boolean', markdownDescription: nls.localize('useIgnoreFiles', "Controls whether to use `.gitignore` and `.ignore` files when searching for files."), @@ -663,7 +670,12 @@ configurationRegistry.registerConfiguration({ 'search.runInExtensionHost': { type: 'boolean', default: false, - description: nls.localize('search.searchRipgrepEnable', "Whether to run search in the extension host") + description: nls.localize('search.runInExtensionHost', "Whether to run search in the extension host. Requires a restart to take effect.") + }, + 'search.usePCRE2': { + type: 'boolean', + default: false, + description: nls.localize('search.usePCRE2', "Whether to use the PCRE2 regex engine in text search. This enables using some advaned regex features like lookbehind and backreferences.") } } }); diff --git a/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts b/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts index 95b77c6a04f..9186a2bcefa 100644 --- a/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts +++ b/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts @@ -9,7 +9,7 @@ import * as objects from 'vs/base/common/objects'; import { TPromise } from 'vs/base/common/winjs.base'; import { CacheState } from 'vs/workbench/parts/search/browser/openFileHandler'; import { DeferredTPromise } from 'vs/base/test/common/utils'; -import { QueryType, ISearchQuery } from 'vs/platform/search/common/search'; +import { QueryType, IFileQuery } from 'vs/platform/search/common/search'; suite('CacheState', () => { @@ -178,16 +178,16 @@ suite('CacheState', () => { public loading: { [cacheKey: string]: DeferredTPromise } = {}; public disposing: { [cacheKey: string]: DeferredTPromise } = {}; - public baseQuery: ISearchQuery = { + public baseQuery: IFileQuery = { type: QueryType.File }; - public query(cacheKey: string): ISearchQuery { + public query(cacheKey: string): IFileQuery { this.cacheKeys.push(cacheKey); return objects.assign({ cacheKey: cacheKey }, this.baseQuery); } - public load(query: ISearchQuery): TPromise { + public load(query: IFileQuery): TPromise { const promise = new DeferredTPromise(); this.loading[query.cacheKey] = promise; return promise; diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index 70ea4a59a83..c7d5ae3fef6 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -10,14 +10,15 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IFolderQuery, IPatternInfo, ISearchQuery, QueryType } from 'vs/platform/search/common/search'; +import { IFolderQuery, IPatternInfo, QueryType, ITextQuery, IFileQuery } from 'vs/platform/search/common/search'; import { IWorkspaceContextService, toWorkspaceFolders, Workspace } from 'vs/platform/workspace/common/workspace'; import { ISearchPathsResult, QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; import { TestContextService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices'; const DEFAULT_EDITOR_CONFIG = {}; const DEFAULT_USER_CONFIG = { useRipgrep: true, useIgnoreFiles: true, useGlobalIgnoreFiles: true }; -const DEFAULT_QUERY_PROPS = { useRipgrep: true, disregardIgnoreFiles: false, disregardGlobalIgnoreFiles: false }; +const DEFAULT_QUERY_PROPS = { useRipgrep: true }; +const DEFAULT_TEXT_QUERY_PROPS = { usePCRE2: false }; suite('QueryBuilder', () => { const PATTERN_INFO: IPatternInfo = { pattern: 'a' }; @@ -49,9 +50,9 @@ suite('QueryBuilder', () => { }); test('simple text pattern', () => { - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text(PATTERN_INFO), - { + { folderQueries: [], contentPattern: PATTERN_INFO, type: QueryType.Text @@ -59,12 +60,12 @@ suite('QueryBuilder', () => { }); test('folderResources', () => { - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI] ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI }], type: QueryType.Text @@ -82,12 +83,12 @@ suite('QueryBuilder', () => { } }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI] ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI, @@ -103,13 +104,13 @@ suite('QueryBuilder', () => { }); test('simple include', () => { - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { includePattern: './bar' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: getUri(fixPath(paths.join(ROOT_1, 'bar'))) @@ -117,13 +118,13 @@ suite('QueryBuilder', () => { type: QueryType.Text }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { includePattern: '.\\bar' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: getUri(fixPath(paths.join(ROOT_1, 'bar'))) @@ -143,13 +144,13 @@ suite('QueryBuilder', () => { } }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { includePattern: './foo' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: getUri(paths.join(ROOT_1, 'foo')) @@ -183,12 +184,12 @@ suite('QueryBuilder', () => { }, ROOT_2_URI); // There are 3 roots, the first two have search.exclude settings, test that the correct basic query is returned - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI, ROOT_2_URI, ROOT_3_URI] ), - { + { contentPattern: PATTERN_INFO, folderQueries: [ { folder: ROOT_1_URI, excludePattern: patternsToIExpression('foo/**/*.js') }, @@ -200,13 +201,13 @@ suite('QueryBuilder', () => { ); // Now test that it merges the root excludes when an 'include' is used - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI, ROOT_2_URI, ROOT_3_URI], { includePattern: './root2/src' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [ { folder: getUri(paths.join(ROOT_2, 'src')) } @@ -218,13 +219,13 @@ suite('QueryBuilder', () => { }); test('simple exclude input pattern', () => { - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { excludePattern: 'foo' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -237,27 +238,25 @@ suite('QueryBuilder', () => { test('file pattern trimming', () => { const content = 'content'; assertEqualQueries( - queryBuilder.text( - PATTERN_INFO, + queryBuilder.file( undefined, { filePattern: ` ${content} ` } ), - { + { folderQueries: [], - contentPattern: PATTERN_INFO, filePattern: content, - type: QueryType.Text + type: QueryType.File }); }); test('exclude ./ syntax', () => { - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { excludePattern: './bar' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -266,13 +265,13 @@ suite('QueryBuilder', () => { type: QueryType.Text }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { excludePattern: './bar/**/*.ts' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -281,13 +280,13 @@ suite('QueryBuilder', () => { type: QueryType.Text }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { excludePattern: '.\\bar\\**\\*.ts' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -298,13 +297,13 @@ suite('QueryBuilder', () => { }); test('extraFileResources', () => { - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { extraFileResources: [getUri('/foo/bar.js')] } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -313,7 +312,7 @@ suite('QueryBuilder', () => { type: QueryType.Text }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], @@ -322,7 +321,7 @@ suite('QueryBuilder', () => { excludePattern: '*.js' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -331,7 +330,7 @@ suite('QueryBuilder', () => { type: QueryType.Text }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], @@ -340,7 +339,7 @@ suite('QueryBuilder', () => { includePattern: '*.txt' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -766,7 +765,16 @@ suite('QueryBuilder', () => { }); }); -function assertEqualQueries(actual: ISearchQuery, expected: ISearchQuery): void { +function assertEqualTextQueries(actual: ITextQuery, expected: ITextQuery): void { + expected = { + ...DEFAULT_TEXT_QUERY_PROPS, + ...expected + }; + + return assertEqualQueries(actual, expected); +} + +function assertEqualQueries(actual: ITextQuery | IFileQuery, expected: ITextQuery | IFileQuery): void { expected = { ...DEFAULT_QUERY_PROPS, ...expected @@ -781,8 +789,6 @@ function assertEqualQueries(actual: ISearchQuery, expected: ISearchQuery): void }; }; - delete actual.ignoreSymlinks; - // Avoid comparing URI objects, not a good idea if (expected.folderQueries) { assert.deepEqual(actual.folderQueries.map(folderQueryToCompareObject), expected.folderQueries.map(folderQueryToCompareObject)); diff --git a/src/vs/workbench/parts/search/test/common/searchModel.test.ts b/src/vs/workbench/parts/search/test/common/searchModel.test.ts index c54dad1e84f..920fb34e529 100644 --- a/src/vs/workbench/parts/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchModel.test.ts @@ -71,7 +71,7 @@ suite('SearchModel', () => { instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModelService, stubModelService(instantiationService)); instantiationService.stub(ISearchService, {}); - instantiationService.stub(ISearchService, 'search', TPromise.as({ results: [] })); + instantiationService.stub(ISearchService, 'textSearch', TPromise.as({ results: [] })); }); teardown(() => { @@ -82,7 +82,7 @@ suite('SearchModel', () => { function searchServiceWithResults(results: IFileMatch[], complete: ISearchComplete | null = null): ISearchService { return { - search(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise { + textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise { return new TPromise(resolve => { process.nextTick(() => { results.forEach(onProgress); @@ -95,7 +95,7 @@ suite('SearchModel', () => { function searchServiceWithError(error: Error): ISearchService { return { - search(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise { + textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise { return new TPromise((resolve, reject) => { reject(error); }); @@ -105,7 +105,7 @@ suite('SearchModel', () => { function canceleableSearchService(tokenSource: CancellationTokenSource): ISearchService { return { - search(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise { + textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise { if (token) { token.onCancellationRequested(() => tokenSource.cancel()); } @@ -237,7 +237,7 @@ suite('SearchModel', () => { instantiationService.stub(ITelemetryService, 'publicLog', target1); let promise = new DeferredTPromise(); - instantiationService.stub(ISearchService, 'search', promise); + instantiationService.stub(ISearchService, 'textSearch', promise); let testObject = instantiationService.createInstance(SearchModel); let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); diff --git a/src/vs/workbench/parts/stats/node/workspaceStats.ts b/src/vs/workbench/parts/stats/node/workspaceStats.ts index e6380eace34..9d27b1cee8e 100644 --- a/src/vs/workbench/parts/stats/node/workspaceStats.ts +++ b/src/vs/workbench/parts/stats/node/workspaceStats.ts @@ -54,11 +54,15 @@ const ModulesToLookFor = [ 'react', 'react-native', '@angular/core', + '@ionic', 'vue', + 'tns-core-modules', // Other interesting packages 'aws-sdk', + 'aws-amplify', 'azure', 'azure-storage', + 'firebase', '@google-cloud/common', 'heroku-cli' ]; @@ -259,9 +263,11 @@ export class WorkspaceStats implements IWorkbenchContribution { "workspace.npm.@angular/core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.vue" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.aws-sdk" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.aws-amplify-sdk" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.azure-storage" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.@google-cloud/common" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.firebase" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.heroku-cli" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.bower" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.yeoman.code.ext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -271,6 +277,8 @@ export class WorkspaceStats implements IWorkbenchContribution { "workspace.xamarin.ios" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.android.cpp" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.reactNative" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.ionic" : { "classification" : "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": "true" }, + "workspace.nativeScript" : { "classification" : "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": "true" }, "workspace.py.requirements" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.requirements.star" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.Pipfile" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -385,6 +393,14 @@ export class WorkspaceStats implements IWorkbenchContribution { } } + if (tags['workspace.config.xml'] && + !tags['workspace.language.cs'] && !tags['workspace.language.vb'] && !tags['workspace.language.aspx']) { + + if (nameSet.has('ionic.config.json')) { + tags['workspace.ionic'] = true; + } + } + if (mainActivity && properties && resources) { tags['workspace.xamarin.android'] = true; } @@ -461,6 +477,10 @@ export class WorkspaceStats implements IWorkbenchContribution { if (packageJsonContents['dependencies'][module]) { tags['workspace.reactNative'] = true; } + } else if ('tns-core-modules' === module) { + if (packageJsonContents['dependencies'][module]) { + tags['workspace.nativescript'] = true; + } } else { if (packageJsonContents['dependencies'][module]) { tags['workspace.npm.' + module] = true; @@ -675,4 +695,4 @@ export class WorkspaceStats implements IWorkbenchContribution { this.reportAzure(uris); } } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/tasks/common/problemMatcher.ts b/src/vs/workbench/parts/tasks/common/problemMatcher.ts index 6bf21c96387..9c6a0d5c064 100644 --- a/src/vs/workbench/parts/tasks/common/problemMatcher.ts +++ b/src/vs/workbench/parts/tasks/common/problemMatcher.ts @@ -29,7 +29,7 @@ export enum FileLocationKind { } export module FileLocationKind { - export function fromString(value: string): FileLocationKind { + export function fromString(value: string): FileLocationKind | undefined { value = value.toLowerCase(); if (value === 'absolute') { return FileLocationKind.Absolute; @@ -41,14 +41,13 @@ export module FileLocationKind { } } - export enum ProblemLocationKind { File, Location } export module ProblemLocationKind { - export function fromString(value: string): ProblemLocationKind { + export function fromString(value: string): ProblemLocationKind | undefined { value = value.toLowerCase(); if (value === 'file') { return ProblemLocationKind.File; @@ -110,7 +109,7 @@ export enum ApplyToKind { } export module ApplyToKind { - export function fromString(value: string): ApplyToKind { + export function fromString(value: string): ApplyToKind | undefined { value = value.toLowerCase(); if (value === 'alldocuments') { return ApplyToKind.allDocuments; @@ -179,18 +178,21 @@ export interface ProblemMatch { } export interface HandleResult { - match: ProblemMatch; + match: ProblemMatch | null; continue: boolean; } export function getResource(filename: string, matcher: ProblemMatcher): URI { let kind = matcher.fileLocation; - let fullPath: string; + let fullPath: string | undefined; if (kind === FileLocationKind.Absolute) { fullPath = filename; - } else if (kind === FileLocationKind.Relative) { + } else if ((kind === FileLocationKind.Relative) && matcher.filePrefix) { fullPath = Paths.join(matcher.filePrefix, filename); } + if (fullPath === void 0) { + throw new Error('FileLocationKind is not actionable. Does the matcher have a filePrefix? This should never happen.'); + } fullPath = fullPath.replace(/\\/g, '/'); if (fullPath[0] !== '/') { fullPath = '/' + fullPath; @@ -204,7 +206,7 @@ export function getResource(filename: string, matcher: ProblemMatcher): URI { export interface ILineMatcher { matchLength: number; - next(line: string): ProblemMatch; + next(line: string): ProblemMatch | null; handle(lines: string[], start?: number): HandleResult; } @@ -230,50 +232,57 @@ abstract class AbstractLineMatcher implements ILineMatcher { return { match: null, continue: false }; } - public next(line: string): ProblemMatch { + public next(line: string): ProblemMatch | null { return null; } public abstract get matchLength(): number; - protected fillProblemData(data: ProblemData, pattern: ProblemPattern, matches: RegExpExecArray): void { - this.fillProperty(data, 'file', pattern, matches, true); - this.appendProperty(data, 'message', pattern, matches, true); - this.fillProperty(data, 'code', pattern, matches, true); - this.fillProperty(data, 'severity', pattern, matches, true); - this.fillProperty(data, 'location', pattern, matches, true); - this.fillProperty(data, 'line', pattern, matches); - this.fillProperty(data, 'character', pattern, matches); - this.fillProperty(data, 'endLine', pattern, matches); - this.fillProperty(data, 'endCharacter', pattern, matches); + protected fillProblemData(data: ProblemData | null, pattern: ProblemPattern, matches: RegExpExecArray): data is ProblemData { + if (data) { + this.fillProperty(data, 'file', pattern, matches, true); + this.appendProperty(data, 'message', pattern, matches, true); + this.fillProperty(data, 'code', pattern, matches, true); + this.fillProperty(data, 'severity', pattern, matches, true); + this.fillProperty(data, 'location', pattern, matches, true); + this.fillProperty(data, 'line', pattern, matches); + this.fillProperty(data, 'character', pattern, matches); + this.fillProperty(data, 'endLine', pattern, matches); + this.fillProperty(data, 'endCharacter', pattern, matches); + return true; + } else { + return false; + } } private appendProperty(data: ProblemData, property: keyof ProblemData, pattern: ProblemPattern, matches: RegExpExecArray, trim: boolean = false): void { + const patternProperty = pattern[property]; if (Types.isUndefined(data[property])) { this.fillProperty(data, property, pattern, matches, trim); } - else if (!Types.isUndefined(pattern[property]) && pattern[property] < matches.length) { - let value = matches[pattern[property]]; + else if (!Types.isUndefined(patternProperty) && patternProperty < matches.length) { + let value = matches[patternProperty]; if (trim) { - value = Strings.trim(value); + value = Strings.trim(value)!; } data[property] += endOfLine + value; } } private fillProperty(data: ProblemData, property: keyof ProblemData, pattern: ProblemPattern, matches: RegExpExecArray, trim: boolean = false): void { - if (Types.isUndefined(data[property]) && !Types.isUndefined(pattern[property]) && pattern[property] < matches.length) { - let value = matches[pattern[property]]; + const patternAtProperty = pattern[property]; + if (Types.isUndefined(data[property]) && !Types.isUndefined(patternAtProperty) && patternAtProperty < matches.length) { + let value = matches[patternAtProperty]; if (value !== void 0) { if (trim) { - value = Strings.trim(value); + value = Strings.trim(value)!; } data[property] = value; } } } - protected getMarkerMatch(data: ProblemData): ProblemMatch { + protected getMarkerMatch(data: ProblemData): ProblemMatch | undefined { try { let location = this.getLocation(data); if (data.file && location && data.message) { @@ -307,7 +316,7 @@ abstract class AbstractLineMatcher implements ILineMatcher { return getResource(filename, this.matcher); } - private getLocation(data: ProblemData): Location { + private getLocation(data: ProblemData): Location | null { if (data.kind === ProblemLocationKind.File) { return this.createLocation(0, 0, 0, 0); } @@ -324,7 +333,7 @@ abstract class AbstractLineMatcher implements ILineMatcher { return this.createLocation(startLine, startColumn, endLine, endColumn); } - private parseLocationInfo(value: string): Location { + private parseLocationInfo(value: string): Location | null { if (!value || !value.match(/(\d+|\d+,\d+|\d+,\d+,\d+,\d+)/)) { return null; } @@ -338,7 +347,7 @@ abstract class AbstractLineMatcher implements ILineMatcher { } } - private createLocation(startLine: number, startColumn: number, endLine: number, endColumn: number): Location { + private createLocation(startLine: number, startColumn: number | undefined, endLine: number | undefined, endColumn: number | undefined): Location { if (startLine && startColumn && endColumn) { return { startLineNumber: startLine, startCharacter: startColumn, endLineNumber: endLine || startLine, endCharacter: endColumn }; } @@ -406,7 +415,7 @@ class SingleLineMatcher extends AbstractLineMatcher { return { match: null, continue: false }; } - public next(line: string): ProblemMatch { + public next(line: string): ProblemMatch | null { return null; } } @@ -414,7 +423,7 @@ class SingleLineMatcher extends AbstractLineMatcher { class MultiLineMatcher extends AbstractLineMatcher { private patterns: ProblemPattern[]; - private data: ProblemData; + private data: ProblemData | null; constructor(matcher: ProblemMatcher) { super(matcher); @@ -428,7 +437,7 @@ class MultiLineMatcher extends AbstractLineMatcher { public handle(lines: string[], start: number = 0): HandleResult { Assert.ok(lines.length - start === this.patterns.length); this.data = Object.create(null); - let data = this.data; + let data = this.data!; data.kind = this.patterns[0].kind; for (let i = 0; i < this.patterns.length; i++) { let pattern = this.patterns[i]; @@ -443,14 +452,15 @@ class MultiLineMatcher extends AbstractLineMatcher { this.fillProblemData(data, pattern, matches); } } - let loop = this.patterns[this.patterns.length - 1].loop; + let loop = !!this.patterns[this.patterns.length - 1].loop; if (!loop) { this.data = null; } - return { match: this.getMarkerMatch(data), continue: loop }; + const markerMatch = data ? this.getMarkerMatch(data) : null; + return { match: markerMatch ? markerMatch : null, continue: loop }; } - public next(line: string): ProblemMatch { + public next(line: string): ProblemMatch | null { let pattern = this.patterns[this.patterns.length - 1]; Assert.ok(pattern.loop === true && this.data !== null); let matches = pattern.regexp.exec(line); @@ -459,8 +469,11 @@ class MultiLineMatcher extends AbstractLineMatcher { return null; } let data = Objects.deepClone(this.data); - this.fillProblemData(data, pattern, matches); - return this.getMarkerMatch(data); + let problemMatch: ProblemMatch | undefined; + if (this.fillProblemData(data, pattern, matches)) { + problemMatch = this.getMarkerMatch(data); + } + return problemMatch ? problemMatch : null; } } @@ -552,6 +565,21 @@ export namespace Config { loop?: boolean; } + export interface CheckedProblemPattern extends ProblemPattern { + /** + * The regular expression to find a problem in the console output of an + * executed task. + */ + regexp: string; + } + + export namespace CheckedProblemPattern { + export function is(value: any): value is CheckedProblemPattern { + let candidate: ProblemPattern = value as ProblemPattern; + return candidate && Types.isString(candidate.regexp); + } + } + export interface NamedProblemPattern extends ProblemPattern { /** * The name of the problem pattern. @@ -565,12 +593,27 @@ export namespace Config { } export namespace NamedProblemPattern { - export function is(value: ProblemPattern): value is NamedProblemPattern { + export function is(value: any): value is NamedProblemPattern { let candidate: NamedProblemPattern = value as NamedProblemPattern; return candidate && Types.isString(candidate.name); } } + export interface NamedCheckedProblemPattern extends NamedProblemPattern { + /** + * The regular expression to find a problem in the console output of an + * executed task. + */ + regexp: string; + } + + export namespace NamedCheckedProblemPattern { + export function is(value: any): value is NamedCheckedProblemPattern { + let candidate: NamedProblemPattern = value as NamedProblemPattern; + return candidate && NamedProblemPattern.is(candidate) && Types.isString(candidate.regexp); + } + } + export type MultiLineProblemPattern = ProblemPattern[]; export namespace MultiLineProblemPattern { @@ -579,7 +622,24 @@ export namespace Config { } } - export interface NamedMultiLineProblemPattern { + export type MultiLineCheckedProblemPattern = CheckedProblemPattern[]; + + export namespace MultiLineCheckedProblemPattern { + export function is(value: any): value is MultiLineCheckedProblemPattern { + let is = false; + if (value && Types.isArray(value)) { + is = true; + value.forEach(element => { + if (!Config.CheckedProblemPattern.is(element)) { + is = false; + } + }); + } + return is; + } + } + + export interface NamedMultiLineCheckedProblemPattern { /** * The name of the problem pattern. */ @@ -593,17 +653,17 @@ export namespace Config { /** * The actual patterns */ - patterns: MultiLineProblemPattern; + patterns: MultiLineCheckedProblemPattern; } - export namespace NamedMultiLineProblemPattern { - export function is(value: any): value is NamedMultiLineProblemPattern { - let candidate = value as NamedMultiLineProblemPattern; - return candidate && Types.isString(candidate.name) && Types.isArray(candidate.patterns); + export namespace NamedMultiLineCheckedProblemPattern { + export function is(value: any): value is NamedMultiLineCheckedProblemPattern { + let candidate = value as NamedMultiLineCheckedProblemPattern; + return candidate && Types.isString(candidate.name) && Types.isArray(candidate.patterns) && MultiLineCheckedProblemPattern.is(candidate.patterns); } } - export type NamedProblemPatterns = (Config.NamedProblemPattern | Config.NamedMultiLineProblemPattern)[]; + export type NamedProblemPatterns = (Config.NamedProblemPattern | Config.NamedMultiLineCheckedProblemPattern)[]; /** * A watching pattern @@ -737,10 +797,10 @@ export namespace Config { export interface NamedProblemMatcher extends ProblemMatcher { /** - * An optional name. This name can be used to refer to the + * This name can be used to refer to the * problem matcher from within a task. */ - name?: string; + name: string; /** * A human readable label. @@ -762,24 +822,29 @@ export class ProblemPatternParser extends Parser { public parse(value: Config.ProblemPattern): ProblemPattern; public parse(value: Config.MultiLineProblemPattern): MultiLineProblemPattern; public parse(value: Config.NamedProblemPattern): NamedProblemPattern; - public parse(value: Config.NamedMultiLineProblemPattern): NamedMultiLineProblemPattern; - public parse(value: Config.ProblemPattern | Config.MultiLineProblemPattern | Config.NamedProblemPattern | Config.NamedMultiLineProblemPattern): any { - if (Config.NamedMultiLineProblemPattern.is(value)) { + public parse(value: Config.NamedMultiLineCheckedProblemPattern): NamedMultiLineProblemPattern; + public parse(value: Config.ProblemPattern | Config.MultiLineProblemPattern | Config.NamedProblemPattern | Config.NamedMultiLineCheckedProblemPattern): any { + if ((Config.MultiLineProblemPattern.is(value) && !Config.MultiLineCheckedProblemPattern.is(value)) || + (!Config.MultiLineProblemPattern.is(value) && !Config.CheckedProblemPattern.is(value))) { + this.error(localize('ProblemPatternParser.problemPattern.missingRegExp', 'The problem pattern is missing a regular expression.')); + } + + if (Config.NamedMultiLineCheckedProblemPattern.is(value)) { return this.createNamedMultiLineProblemPattern(value); - } else if (Config.MultiLineProblemPattern.is(value)) { + } else if (Config.MultiLineCheckedProblemPattern.is(value)) { return this.createMultiLineProblemPattern(value); - } else if (Config.NamedProblemPattern.is(value)) { + } else if (Config.NamedCheckedProblemPattern.is(value)) { let result = this.createSingleProblemPattern(value) as NamedProblemPattern; result.name = value.name; return result; - } else if (value) { + } else if (Config.CheckedProblemPattern.is(value)) { return this.createSingleProblemPattern(value); } else { return null; } } - private createSingleProblemPattern(value: Config.ProblemPattern): ProblemPattern { + private createSingleProblemPattern(value: Config.CheckedProblemPattern): ProblemPattern | null { let result = this.doCreateSingleProblemPattern(value, true); if (result.kind === undefined) { result.kind = ProblemLocationKind.Location; @@ -787,16 +852,20 @@ export class ProblemPatternParser extends Parser { return this.validateProblemPattern([result]) ? result : null; } - private createNamedMultiLineProblemPattern(value: Config.NamedMultiLineProblemPattern): NamedMultiLineProblemPattern { + private createNamedMultiLineProblemPattern(value: Config.NamedMultiLineCheckedProblemPattern): NamedMultiLineProblemPattern | null { + const validPatterns = this.createMultiLineProblemPattern(value.patterns); + if (!validPatterns) { + return null; + } let result = { name: value.name, label: value.label ? value.label : value.name, - patterns: this.createMultiLineProblemPattern(value.patterns) + patterns: validPatterns }; - return result.patterns ? result : null; + return result; } - private createMultiLineProblemPattern(values: Config.MultiLineProblemPattern): MultiLineProblemPattern { + private createMultiLineProblemPattern(values: Config.MultiLineCheckedProblemPattern): MultiLineProblemPattern | null { let result: MultiLineProblemPattern = []; for (let i = 0; i < values.length; i++) { let pattern = this.doCreateSingleProblemPattern(values[i], false); @@ -814,10 +883,12 @@ export class ProblemPatternParser extends Parser { return this.validateProblemPattern(result) ? result : null; } - private doCreateSingleProblemPattern(value: Config.ProblemPattern, setDefaults: boolean): ProblemPattern { - let result: ProblemPattern = { - regexp: this.createRegularExpression(value.regexp), - }; + private doCreateSingleProblemPattern(value: Config.CheckedProblemPattern, setDefaults: boolean): ProblemPattern { + const regexp = this.createRegularExpression(value.regexp); + if (regexp === void 0) { + throw new Error('Invalid regular expression'); + } + let result: ProblemPattern = { regexp }; if (value.kind) { result.kind = ProblemLocationKind.fromString(value.kind); } @@ -861,8 +932,7 @@ export class ProblemPatternParser extends Parser { } private validateProblemPattern(values: ProblemPattern[]): boolean { - let file: boolean, message: boolean, location: boolean, line: boolean; - let regexp: number = 0; + let file: boolean = false, message: boolean = false, location: boolean = false, line: boolean = false; let locationKind = (values[0].kind === undefined) ? ProblemLocationKind.Location : values[0].kind; values.forEach((pattern, i) => { @@ -873,14 +943,7 @@ export class ProblemPatternParser extends Parser { message = message || !Types.isUndefined(pattern.message); location = location || !Types.isUndefined(pattern.location); line = line || !Types.isUndefined(pattern.line); - if (pattern.regexp) { - regexp++; - } }); - if (regexp !== values.length) { - this.error(localize('ProblemPatternParser.problemPattern.missingRegExp', 'The problem pattern is missing a regular expression.')); - return false; - } if (!(file && message)) { this.error(localize('ProblemPatternParser.problemPattern.missingProperty', 'The problem pattern is invalid. It must have at least have a file and a message.')); return false; @@ -892,11 +955,8 @@ export class ProblemPatternParser extends Parser { return true; } - private createRegularExpression(value: string): RegExp { - let result: RegExp | null = null; - if (!value) { - return result; - } + private createRegularExpression(value: string): RegExp | undefined { + let result: RegExp | undefined; try { result = new RegExp(value); } catch (err) { @@ -999,13 +1059,12 @@ export namespace Schemas { }; export const NamedProblemPattern: IJSONSchema = Objects.deepClone(ProblemPattern); - NamedProblemPattern.properties = Objects.deepClone(NamedProblemPattern.properties); + NamedProblemPattern.properties = Objects.deepClone(NamedProblemPattern.properties) || {}; NamedProblemPattern.properties['name'] = { type: 'string', description: localize('NamedProblemPatternSchema.name', 'The name of the problem pattern.') }; - export const MultiLineProblemPattern: IJSONSchema = { type: 'array', items: ProblemPattern @@ -1061,7 +1120,7 @@ class ProblemPatternRegistryImpl implements IProblemPatternRegistry { let problemPatterns = extension.value as Config.NamedProblemPatterns; let parser = new ProblemPatternParser(new ExtensionRegistryReporter(extension.collector)); for (let pattern of problemPatterns) { - if (Config.NamedMultiLineProblemPattern.is(pattern)) { + if (Config.NamedMultiLineCheckedProblemPattern.is(pattern)) { let result = parser.parse(pattern); if (parser.problemReporter.status.state < ValidationState.Error) { this.add(result.name, result.patterns); @@ -1225,7 +1284,7 @@ export class ProblemMatcherParser extends Parser { super(logger); } - public parse(json: Config.ProblemMatcher): ProblemMatcher { + public parse(json: Config.ProblemMatcher): ProblemMatcher | null { let result = this.createProblemMatcher(json); if (!this.checkProblemMatcherValid(json, result)) { return null; @@ -1235,7 +1294,7 @@ export class ProblemMatcherParser extends Parser { return result; } - private checkProblemMatcherValid(externalProblemMatcher: Config.ProblemMatcher, problemMatcher: ProblemMatcher): boolean { + private checkProblemMatcherValid(externalProblemMatcher: Config.ProblemMatcher, problemMatcher: ProblemMatcher | null): problemMatcher is ProblemMatcher { if (!problemMatcher) { this.error(localize('ProblemMatcherParser.noProblemMatcher', 'Error: the description can\'t be converted into a problem matcher:\n{0}\n', JSON.stringify(externalProblemMatcher, null, 4))); return false; @@ -1255,7 +1314,7 @@ export class ProblemMatcherParser extends Parser { return true; } - private createProblemMatcher(description: Config.ProblemMatcher): ProblemMatcher { + private createProblemMatcher(description: Config.ProblemMatcher): ProblemMatcher | null { let result: ProblemMatcher | null = null; let owner = description.owner ? description.owner : UUID.generateUuid(); @@ -1267,7 +1326,7 @@ export class ProblemMatcherParser extends Parser { let fileLocation: FileLocationKind | undefined = undefined; let filePrefix: string | undefined = undefined; - let kind: FileLocationKind; + let kind: FileLocationKind | undefined; if (Types.isUndefined(description.fileLocation)) { fileLocation = FileLocationKind.Relative; filePrefix = '${workspaceFolder}'; @@ -1293,6 +1352,9 @@ export class ProblemMatcherParser extends Parser { } let pattern = description.pattern ? this.createProblemPattern(description.pattern) : undefined; + if (!pattern) { + return null; + } let severity = description.severity ? Severity.fromValue(description.severity) : undefined; if (severity === Severity.Ignore) { @@ -1353,7 +1415,7 @@ export class ProblemMatcherParser extends Parser { return result; } - private createProblemPattern(value: string | Config.ProblemPattern | Config.MultiLineProblemPattern): ProblemPattern | ProblemPattern[] { + private createProblemPattern(value: string | Config.ProblemPattern | Config.MultiLineProblemPattern): ProblemPattern | ProblemPattern[] | null { if (Types.isString(value)) { let variableName: string = value; if (variableName.length > 1 && variableName[0] === '$') { @@ -1395,8 +1457,8 @@ export class ProblemMatcherParser extends Parser { if (Types.isUndefinedOrNull(backgroundMonitor)) { return; } - let begins: WatchingPattern = this.createWatchingPattern(backgroundMonitor.beginsPattern); - let ends: WatchingPattern = this.createWatchingPattern(backgroundMonitor.endsPattern); + let begins: WatchingPattern | null = this.createWatchingPattern(backgroundMonitor.beginsPattern); + let ends: WatchingPattern | null = this.createWatchingPattern(backgroundMonitor.endsPattern); if (begins && ends) { internal.watching = { activeOnStart: Types.isBoolean(backgroundMonitor.activeOnStart) ? backgroundMonitor.activeOnStart : false, @@ -1410,12 +1472,12 @@ export class ProblemMatcherParser extends Parser { } } - private createWatchingPattern(external: string | Config.WatchingPattern): WatchingPattern { + private createWatchingPattern(external: string | Config.WatchingPattern | undefined): WatchingPattern | null { if (Types.isUndefinedOrNull(external)) { return null; } - let regexp: RegExp; - let file: number; + let regexp: RegExp | null; + let file: number | undefined; if (Types.isString(external)) { regexp = this.createRegularExpression(external); } else { @@ -1430,7 +1492,7 @@ export class ProblemMatcherParser extends Parser { return file ? { regexp, file } : { regexp, file: 1 }; } - private createRegularExpression(value: string): RegExp { + private createRegularExpression(value: string | undefined): RegExp | null { let result: RegExp | null = null; if (!value) { return result; @@ -1579,7 +1641,7 @@ export namespace Schemas { }; export const LegacyProblemMatcher: IJSONSchema = Objects.deepClone(ProblemMatcher); - LegacyProblemMatcher.properties = Objects.deepClone(LegacyProblemMatcher.properties); + LegacyProblemMatcher.properties = Objects.deepClone(LegacyProblemMatcher.properties) || {}; LegacyProblemMatcher.properties['watchedTaskBeginsRegExp'] = { type: 'string', deprecationMessage: localize('LegacyProblemMatcherSchema.watchedBegin.deprecated', 'This property is deprecated. Use the watching property instead.'), @@ -1592,7 +1654,7 @@ export namespace Schemas { }; export const NamedProblemMatcher: IJSONSchema = Objects.deepClone(ProblemMatcher); - NamedProblemMatcher.properties = Objects.deepClone(NamedProblemMatcher.properties); + NamedProblemMatcher.properties = Objects.deepClone(NamedProblemMatcher.properties) || {}; NamedProblemMatcher.properties.name = { type: 'string', description: localize('NamedProblemMatcherSchema.name', 'The name of the problem matcher used to refer to it.') diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index e936a5ca7cc..4fc3965b6a6 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -531,7 +531,7 @@ export class TerminalTaskSystem implements ITaskSystem { } else if (task.command.presentation.showReuseMessage) { waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.'); } else { - waitOnExit = '\u200B'; + waitOnExit = true; } } let shellLaunchConfig: IShellLaunchConfig | undefined = undefined; @@ -694,6 +694,11 @@ export class TerminalTaskSystem implements ITaskSystem { delete this.terminals[terminalKey]; delete this.sameTaskTerminals[terminalData.lastTask]; this.idleTaskTerminals.delete(terminalData.lastTask); + // Delete the task now as a work around for cases when the onExit isn't fired. + // This can happen if the terminal wasn't shutdown with an "immediate" flag and is expected. + // For correct terminal re-use, the task needs to be deleted immediately. + // Note that this shouldn't be a problem anymore since user initiated terminal kills are now immediate. + delete this.activeTasks[Task.getMapKey(task)]; } }); this.terminals[terminalKey] = { terminal: result, lastTask: taskKey }; diff --git a/src/vs/workbench/parts/terminal/browser/terminalTab.ts b/src/vs/workbench/parts/terminal/browser/terminalTab.ts index f3a0ebc8e67..48e89fa8845 100644 --- a/src/vs/workbench/parts/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/parts/terminal/browser/terminalTab.ts @@ -61,7 +61,7 @@ class SplitPaneContainer { } // Get sizes - const sizes = []; + const sizes: number[] = []; for (let i = 0; i < this._splitView.length; i++) { sizes.push(this._splitView.getViewSize(i)); } diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 8ecfea8987f..80caa09bead 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -410,10 +410,12 @@ export interface ITerminalInstance { /** * Dispose the terminal instance, removing it from the panel/service and freeing up resources. * - * @param isShuttingDown Whether VS Code is shutting down, if so kill any terminal processes - * immediately. + * @param immediate Whether the kill should be immediate or not. Immediate should only be used + * when VS Code is shutting down or in cases where the terminal dispose was user initiated. + * The immediate===false exists to cover an edge case where the final output of the terminal can + * get cut off. If immediate kill any terminal processes immediately. */ - dispose(isShuttingDown?: boolean): void; + dispose(immediate?: boolean): void; /** * Registers a link matcher, allowing custom link patterns to be matched and handled. diff --git a/src/vs/workbench/parts/terminal/common/terminalCommands.ts b/src/vs/workbench/parts/terminal/common/terminalCommands.ts index 2eff4e1f6b0..5ddb88fa3cb 100644 --- a/src/vs/workbench/parts/terminal/common/terminalCommands.ts +++ b/src/vs/workbench/parts/terminal/common/terminalCommands.ts @@ -18,6 +18,7 @@ export const enum TERMINAL_COMMAND_ID { SELECT_ALL = 'workbench.action.terminal.selectAll', DELETE_WORD_LEFT = 'workbench.action.terminal.deleteWordLeft', DELETE_WORD_RIGHT = 'workbench.action.terminal.deleteWordRight', + DELETE_TO_LINE_START = 'workbench.action.terminal.deleteToLineStart', MOVE_TO_LINE_START = 'workbench.action.terminal.moveToLineStart', MOVE_TO_LINE_END = 'workbench.action.terminal.moveToLineEnd', NEW = 'workbench.action.terminal.new', diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts index 18c85aac5e6..6995df3a152 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -17,7 +17,7 @@ import { getDefaultShell } from 'vs/workbench/parts/terminal/node/terminal'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KillTerminalAction, ClearSelectionTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitTerminalAction, SplitInActiveWorkspaceTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, ResizePaneDownTerminalAction, ScrollToPreviousCommandAction, ScrollToNextCommandAction, SelectToPreviousCommandAction, SelectToNextCommandAction, SelectToPreviousLineAction, SelectToNextLineAction, ToggleEscapeSequenceLoggingAction, SendSequenceTerminalCommand, ToggleRegexCommand, ToggleWholeWordCommand, ToggleCaseSensitiveCommand, FindNext, FindPrevious } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; +import { KillTerminalAction, ClearSelectionTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitTerminalAction, SplitInActiveWorkspaceTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, ResizePaneDownTerminalAction, ScrollToPreviousCommandAction, ScrollToNextCommandAction, SelectToPreviousCommandAction, SelectToNextCommandAction, SelectToPreviousLineAction, SelectToNextLineAction, ToggleEscapeSequenceLoggingAction, SendSequenceTerminalCommand, ToggleRegexCommand, ToggleWholeWordCommand, ToggleCaseSensitiveCommand, FindNext, FindPrevious, DeleteToLineStartTerminalAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; import { Registry } from 'vs/platform/registry/common/platform'; import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/commandsHandler'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; @@ -249,6 +249,7 @@ configurationRegistry.registerConfiguration({ TERMINAL_COMMAND_ID.CLEAR_SELECTION, TERMINAL_COMMAND_ID.CLEAR, TERMINAL_COMMAND_ID.COPY_SELECTION, + TERMINAL_COMMAND_ID.DELETE_TO_LINE_START, TERMINAL_COMMAND_ID.DELETE_WORD_LEFT, TERMINAL_COMMAND_ID.DELETE_WORD_RIGHT, TERMINAL_COMMAND_ID.FIND_WIDGET_FOCUS, @@ -498,6 +499,10 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordRightT primary: KeyMod.CtrlCmd | KeyCode.Delete, mac: { primary: KeyMod.Alt | KeyCode.Delete } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Right', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteToLineStartTerminalAction, DeleteToLineStartTerminalAction.ID, DeleteToLineStartTerminalAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete To Line Start', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveToLineStartTerminalAction, MoveToLineStartTerminalAction.ID, MoveToLineStartTerminalAction.LABEL, { primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index 2cdfd3b16a0..b1613c7f2d2 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -95,7 +95,7 @@ export class KillTerminalAction extends Action { public run(event?: any): PromiseLike { const instance = this.terminalService.getActiveInstance(); if (instance) { - instance.dispose(); + instance.dispose(true); if (this.terminalService.terminalInstances.length > 0) { this.terminalService.showPanel(true); } @@ -120,7 +120,7 @@ export class QuickKillTerminalAction extends Action { public run(event?: any): Promise { const instance = this.terminalEntry.instance; if (instance) { - instance.dispose(); + instance.dispose(true); } return Promise.resolve(timeout(50)).then(result => this.quickOpenService.show(TERMINAL_PICKER_PREFIX, null)); } @@ -220,6 +220,20 @@ export class DeleteWordRightTerminalAction extends BaseSendTextTerminalAction { } } +export class DeleteToLineStartTerminalAction extends BaseSendTextTerminalAction { + public static readonly ID = TERMINAL_COMMAND_ID.DELETE_TO_LINE_START; + public static readonly LABEL = nls.localize('workbench.action.terminal.deleteToLineStart', "Delete to Line Start"); + + constructor( + id: string, + label: string, + @ITerminalService terminalService: ITerminalService + ) { + // Send ctrl+u + super(id, label, '\u0015', terminalService); + } +} + export class MoveToLineStartTerminalAction extends BaseSendTextTerminalAction { public static readonly ID = TERMINAL_COMMAND_ID.MOVE_TO_LINE_START; public static readonly LABEL = nls.localize('workbench.action.terminal.moveToLineStart', "Move To Line Start"); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 5d9dcdcef99..296f10cded6 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -576,7 +576,7 @@ export class TerminalInstance implements ITerminalInstance { this._terminalFocusContextKey.set(terminalFocused); } - public dispose(isShuttingDown?: boolean): void { + public dispose(immediate?: boolean): void { this._logService.trace(`terminalInstance#dispose (id: ${this.id})`); this._windowsShellHelper = lifecycle.dispose(this._windowsShellHelper); @@ -602,7 +602,7 @@ export class TerminalInstance implements ITerminalInstance { this._xterm = null; } if (this._processManager) { - this._processManager.dispose(isShuttingDown); + this._processManager.dispose(immediate); } if (!this._isDisposed) { this._isDisposed = true; @@ -834,12 +834,12 @@ export class TerminalInstance implements ITerminalInstance { if (exitCode) { this._xterm.writeln(exitCodeMessage); } - let message = typeof this._shellLaunchConfig.waitOnExit === 'string' - ? this._shellLaunchConfig.waitOnExit - : nls.localize('terminal.integrated.waitOnExit', 'Press any key to close the terminal'); - // Bold the message and add an extra new line to make it stand out from the rest of the output - message = `\n\x1b[1m${message}\x1b[0m`; - this._xterm.writeln(message); + if (typeof this._shellLaunchConfig.waitOnExit === 'string') { + let message = this._shellLaunchConfig.waitOnExit; + // Bold the message and add an extra new line to make it stand out from the rest of the output + message = `\n\x1b[1m${message}\x1b[0m`; + this._xterm.writeln(message); + } // Disable all input if the terminal is exiting and listen for next keypress this._xterm.setOption('disableStdin', true); if (this._xterm.textarea) { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts index 4fb2beb4e61..dedd5fb4e16 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -53,7 +53,7 @@ export class TerminalPanel extends Panel { super(TERMINAL_PANEL_ID, telemetryService, _themeService, storageService); } - public create(parent: HTMLElement): Promise { + public create(parent: HTMLElement): void { super.create(parent); this._parentDomElement = parent; dom.addClass(this._parentDomElement, 'integrated-terminal'); @@ -98,7 +98,6 @@ export class TerminalPanel extends Panel { // Force another layout (first is setContainers) since config has changed this.layout(new dom.Dimension(this._terminalContainer.offsetWidth, this._terminalContainer.offsetHeight)); - return Promise.resolve(void 0); } public layout(dimension?: dom.Dimension): void { @@ -344,4 +343,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { if (hoverForeground) { collector.addRule(`.monaco-workbench .panel.integrated-terminal .terminal-message-widget { color: ${hoverForeground}; }`); } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts index 56342f9fb07..707ddc031cf 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts @@ -72,7 +72,11 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { return this._webviewService.reviveWebview(data.viewType, data.id, data.title, iconPath, data.state, data.options, extensionLocation); } } -function reviveIconPath(data: SerializedIconPath) { +function reviveIconPath(data: SerializedIconPath | undefined) { + if (!data) { + return undefined; + } + const light = reviveUri(data.light); const dark = reviveUri(data.dark); return light && dark ? { light, dark } : undefined; diff --git a/src/vs/workbench/services/configuration/common/configurationModels.ts b/src/vs/workbench/services/configuration/common/configurationModels.ts index f5fbcc43d26..e52a3083e50 100644 --- a/src/vs/workbench/services/configuration/common/configurationModels.ts +++ b/src/vs/workbench/services/configuration/common/configurationModels.ts @@ -208,7 +208,7 @@ export class Configuration extends BaseConfiguration { } compare(other: Configuration): string[] { - const result = []; + const result: string[] = []; for (const key of this.allKeys()) { if (!equals(this.getValue(key), other.getValue(key)) || (this._workspace && this._workspace.folders.some(folder => !equals(this.getValue(key, { resource: folder.uri }), other.getValue(key, { resource: folder.uri }))))) { diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index b1d73ba8d87..3a418401864 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -208,8 +208,8 @@ export class FileService extends Disposable implements IFileService { throw new Error('not implemented'); } - activateProvider(scheme: string): TPromise { - return TPromise.wrapError(new Error('not implemented')); + activateProvider(scheme: string): Thenable { + return Promise.reject(new Error('not implemented')); } canHandleResource(resource: uri): boolean { diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index 18a8e11a001..3738495c314 100644 --- a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -206,7 +206,7 @@ export class RemoteFileService extends FileService { }; } - activateProvider(scheme: string): TPromise { + activateProvider(scheme: string): Thenable { return this._extensionService.activateByEvent('onFileSystem:' + scheme); } diff --git a/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts index 4e907ad9d5d..3fdca226618 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts @@ -68,7 +68,7 @@ export class ChokidarWatcherService implements IWatcherService { public setRoots(requests: IWatcherRequest[]): TPromise { const watchers = Object.create(null); - const newRequests = []; + const newRequests: string[] = []; const requestsByBasePath = normalizeRoots(requests); diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index 374c8b468f5..dbbf13c1a93 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -169,7 +169,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding } private findUnassignedDefaultKeybindingEntryIndex(keybindingItem: ResolvedKeybindingItem, userKeybindingEntries: IUserFriendlyKeybinding[]): number[] { - const indices = []; + const indices: number[] = []; for (let index = 0; index < userKeybindingEntries.length; index++) { if (userKeybindingEntries[index].command === `-${keybindingItem.command}`) { indices.push(index); diff --git a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts index 066627f67f2..06ee1fb614c 100644 --- a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts +++ b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts @@ -157,7 +157,7 @@ export class KeybindingsEditorModel extends EditorModel { } private splitKeybindingWords(wordsSeparatedBySpaces: string[]): string[] { - const result = []; + const result: string[] = []; for (const word of wordsSeparatedBySpaces) { result.push(...word.split('+').filter(w => !!w)); } @@ -314,7 +314,7 @@ class KeybindingItemMatches { let firstPartMatch: KeybindingMatch = {}; let chordPartMatch: KeybindingMatch = {}; - const matchedWords = []; + const matchedWords: number[] = []; let firstPartMatchedWords: number[] = []; let chordPartMatchedWords: number[] = []; let matchFirstPart = true; diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 65c773bc29c..ccb32b3bafc 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -587,7 +587,7 @@ export class DefaultSettings extends Disposable { } private removeEmptySettingsGroups(settingsGroups: ISettingsGroup[]): ISettingsGroup[] { - const result = []; + const result: ISettingsGroup[] = []; for (const settingsGroup of settingsGroups) { settingsGroup.sections = settingsGroup.sections.filter(section => section.settings.length > 0); if (settingsGroup.sections.length) { diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index d3c2f1cc84b..79e240ab07f 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -15,15 +15,16 @@ import * as normalization from 'vs/base/common/normalization'; import * as objects from 'vs/base/common/objects'; import { isEqualOrParent } from 'vs/base/common/paths'; import * as platform from 'vs/base/common/platform'; +import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; import * as types from 'vs/base/common/types'; import { TPromise } from 'vs/base/common/winjs.base'; import * as extfs from 'vs/base/node/extfs'; import * as flow from 'vs/base/node/flow'; import { IProgress, ISearchEngineStats } from 'vs/platform/search/common/search'; +import { IFolderSearch, IRawSearch } from 'vs/workbench/services/search/node/legacy/search'; +import { IRawFileMatch, ISearchEngine, ISearchEngineSuccess } from 'vs/workbench/services/search/node/search'; import { spawnRipgrepCmd } from './ripgrepFileSearch'; -import { IFolderSearch, IRawFileMatch, IRawSearch, ISearchEngine, ISearchEngineSuccess } from './search'; -import { StopWatch } from 'vs/base/common/stopwatch'; enum Traversal { Node = 1, @@ -44,6 +45,11 @@ interface IDirectoryTree { pathToEntries: { [relativePath: string]: IDirectoryEntry[] }; } +const killCmds = new Set<() => void>(); +process.on('exit', () => { + killCmds.forEach(cmd => cmd()); +}); + export class FileWalker { private config: IRawSearch; private useRipgrep: boolean; @@ -190,9 +196,10 @@ export class FileWalker { const isMac = platform.isMacintosh; let cmd: childProcess.ChildProcess; const killCmd = () => cmd && cmd.kill(); + killCmds.add(killCmd); let done = (err?: Error) => { - process.removeListener('exit', killCmd); + killCmds.delete(killCmd); done = () => { }; cb(err); }; @@ -220,7 +227,6 @@ export class FileWalker { cmd = this.spawnFindCmd(folderQuery); } - process.on('exit', killCmd); this.cmdResultCount = 0; this.collectStdout(cmd, 'utf8', useRipgrep, onMessage, (err: Error, stdout?: string, last?: boolean) => { if (err) { @@ -728,7 +734,7 @@ class AbsoluteAndRelativeParsedExpression { } public getBasenameTerms(): string[] { - const basenameTerms = []; + const basenameTerms: string[] = []; if (this.absoluteParsedExpr) { basenameTerms.push(...glob.getBasenameTerms(this.absoluteParsedExpr)); } @@ -741,7 +747,7 @@ class AbsoluteAndRelativeParsedExpression { } public getPathTerms(): string[] { - const pathTerms = []; + const pathTerms: string[] = []; if (this.absoluteParsedExpr) { pathTerms.push(...glob.getPathTerms(this.absoluteParsedExpr)); } diff --git a/src/vs/workbench/services/search/node/fileSearchManager.ts b/src/vs/workbench/services/search/node/fileSearchManager.ts index 8948187d9f0..7acdf19bdc3 100644 --- a/src/vs/workbench/services/search/node/fileSearchManager.ts +++ b/src/vs/workbench/services/search/node/fileSearchManager.ts @@ -11,7 +11,7 @@ import * as resources from 'vs/base/common/resources'; import { StopWatch } from 'vs/base/common/stopwatch'; import { URI } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search'; +import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, IFileQuery } from 'vs/platform/search/common/search'; import { QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/node/search'; import * as vscode from 'vscode'; @@ -48,7 +48,7 @@ class FileSearchEngine { private globalExcludePattern: glob.ParsedExpression; - constructor(private config: ISearchQuery, private provider: vscode.FileSearchProvider) { + constructor(private config: IFileQuery, private provider: vscode.FileSearchProvider) { this.filePattern = config.filePattern; this.includePattern = config.includePattern && glob.parse(config.includePattern); this.maxResults = config.maxResults || null; @@ -189,9 +189,9 @@ class FileSearchEngine { folder: fq.folder, excludes, includes, - useIgnoreFiles: !this.config.disregardIgnoreFiles, - useGlobalIgnoreFiles: !this.config.disregardGlobalIgnoreFiles, - followSymlinks: !this.config.ignoreSymlinks, + useIgnoreFiles: !fq.disregardIgnoreFiles, + useGlobalIgnoreFiles: !fq.disregardGlobalIgnoreFiles, + followSymlinks: !fq.ignoreSymlinks, maxResults: this.config.maxResults }; } @@ -289,7 +289,7 @@ export class FileSearchManager { private static readonly BATCH_SIZE = 512; - fileSearch(config: ISearchQuery, provider: vscode.FileSearchProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise { + fileSearch(config: IFileQuery, provider: vscode.FileSearchProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise { const engine = new FileSearchEngine(config, provider); let resultCount = 0; diff --git a/src/vs/workbench/services/search/node/legacy/rawLegacyTextSearchService.ts b/src/vs/workbench/services/search/node/legacy/rawLegacyTextSearchService.ts new file mode 100644 index 00000000000..aa895c1d4fe --- /dev/null +++ b/src/vs/workbench/services/search/node/legacy/rawLegacyTextSearchService.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import * as gracefulFs from 'graceful-fs'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { MAX_FILE_SIZE } from 'vs/platform/files/node/files'; +import { FileWalker } from 'vs/workbench/services/search/node/fileSearch'; +import { IRawSearch } from 'vs/workbench/services/search/node/legacy/search'; +import { BatchedCollector } from 'vs/workbench/services/search/node/textSearchManager'; +import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/legacy/textSearchWorkerProvider'; +import { ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from '../search'; +import { Engine } from 'vs/workbench/services/search/node/legacy/textSearch'; + +gracefulFs.gracefulify(fs); + +type IProgressCallback = (p: ISerializedSearchProgressItem) => void; + +export class LegacyTextSearchService { + private static readonly BATCH_SIZE = 512; + + private textSearchWorkerProvider: TextSearchWorkerProvider; + + textSearch(config: IRawSearch, progressCallback: IProgressCallback, token: CancellationToken): Promise { + if (!this.textSearchWorkerProvider) { + this.textSearchWorkerProvider = new TextSearchWorkerProvider(); + } + + let engine = new Engine( + config, + new FileWalker({ + folderQueries: config.folderQueries, + extraFiles: config.extraFiles, + includePattern: config.includePattern, + excludePattern: config.excludePattern, + filePattern: config.filePattern, + useRipgrep: false, + maxFilesize: MAX_FILE_SIZE + }), + this.textSearchWorkerProvider); + + return this.doTextSearch(engine, progressCallback, LegacyTextSearchService.BATCH_SIZE, token); + } + + private doTextSearch(engine: Engine, progressCallback: IProgressCallback, batchSize: number, token: CancellationToken): Promise { + token.onCancellationRequested(() => engine.cancel()); + + return new Promise((c, e) => { + // Use BatchedCollector to get new results to the frontend every 2s at least, until 50 results have been returned + const collector = new BatchedCollector(batchSize, progressCallback); + engine.search((matches) => { + const totalMatches = matches.reduce((acc, m) => acc + m.numMatches, 0); + collector.addItems(matches, totalMatches); + }, (progress) => { + progressCallback(progress); + }, (error, stats) => { + collector.flush(); + + if (error) { + e(error); + } else { + c({ + type: 'success', + limitHit: stats.limitHit, + stats: null + }); + } + }); + }); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/legacy/search.ts b/src/vs/workbench/services/search/node/legacy/search.ts new file mode 100644 index 00000000000..23d4710b031 --- /dev/null +++ b/src/vs/workbench/services/search/node/legacy/search.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as glob from 'vs/base/common/glob'; +import { IPatternInfo, ITextSearchPreviewOptions } from 'vs/platform/search/common/search'; + +export interface IFolderSearch { + folder: string; + excludePattern?: glob.IExpression; + includePattern?: glob.IExpression; + fileEncoding?: string; + disregardIgnoreFiles?: boolean; + disregardGlobalIgnoreFiles?: boolean; +} + +export interface IRawSearch { + folderQueries: IFolderSearch[]; + ignoreSymlinks?: boolean; + extraFiles?: string[]; + filePattern?: string; + excludePattern?: glob.IExpression; + includePattern?: glob.IExpression; + contentPattern?: IPatternInfo; + maxResults?: number; + exists?: boolean; + sortByScore?: boolean; + cacheKey?: string; + maxFilesize?: number; + useRipgrep?: boolean; + disregardIgnoreFiles?: boolean; + previewOptions?: ITextSearchPreviewOptions; + disregardGlobalIgnoreFiles?: boolean; +} diff --git a/src/vs/workbench/services/search/node/textSearch.ts b/src/vs/workbench/services/search/node/legacy/textSearch.ts similarity index 97% rename from src/vs/workbench/services/search/node/textSearch.ts rename to src/vs/workbench/services/search/node/legacy/textSearch.ts index c1fdc506ea1..63756c5589b 100644 --- a/src/vs/workbench/services/search/node/textSearch.ts +++ b/src/vs/workbench/services/search/node/legacy/textSearch.ts @@ -7,9 +7,10 @@ import * as path from 'path'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IProgress } from 'vs/platform/search/common/search'; import { FileWalker } from 'vs/workbench/services/search/node/fileSearch'; -import { IRawSearch, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch } from './search'; +import { ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch } from '../search'; import { ITextSearchWorkerProvider } from './textSearchWorkerProvider'; import { ISearchWorker, ISearchWorkerSearchArgs } from './worker/searchWorkerIpc'; +import { IRawSearch } from 'vs/workbench/services/search/node/legacy/search'; export class Engine implements ISearchEngine { diff --git a/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts b/src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts similarity index 94% rename from src/vs/workbench/services/search/node/textSearchWorkerProvider.ts rename to src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts index af095577494..617bd4ed0c4 100644 --- a/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts +++ b/src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts @@ -35,7 +35,7 @@ export class TextSearchWorkerProvider implements ITextSearchWorkerProvider { args: ['--type=searchWorker'], timeout: 30 * 1000, env: { - AMD_ENTRYPOINT: 'vs/workbench/services/search/node/worker/searchWorkerApp', + AMD_ENTRYPOINT: 'vs/workbench/services/search/node/legacy/worker/searchWorkerApp', PIPE_LOGGING: 'true', VERBOSE_LOGGING: process.env.VERBOSE_LOGGING }, diff --git a/src/vs/workbench/services/search/node/worker/searchWorker.ts b/src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts similarity index 99% rename from src/vs/workbench/services/search/node/worker/searchWorker.ts rename to src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts index de39d2f6453..66cd9f8aebc 100644 --- a/src/vs/workbench/services/search/node/worker/searchWorker.ts +++ b/src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts @@ -11,8 +11,8 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { bomLength, decode, detectEncodingFromBuffer, encodingExists, UTF16be, UTF16le, UTF8, UTF8_with_bom } from 'vs/base/node/encoding'; import { Range } from 'vs/editor/common/core/range'; import { ITextSearchPreviewOptions, TextSearchResult } from 'vs/platform/search/common/search'; -import { FileMatch } from '../search'; import { ISearchWorker, ISearchWorkerSearchArgs, ISearchWorkerSearchResult } from './searchWorkerIpc'; +import { FileMatch } from 'vs/workbench/services/search/node/search'; gracefulFs.gracefulify(fs); diff --git a/src/vs/workbench/services/search/node/worker/searchWorkerApp.ts b/src/vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts similarity index 100% rename from src/vs/workbench/services/search/node/worker/searchWorkerApp.ts rename to src/vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts diff --git a/src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts b/src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts similarity index 96% rename from src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts rename to src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts index 46ba13a283f..0e0cd84706a 100644 --- a/src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts +++ b/src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts @@ -5,10 +5,10 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel } from 'vs/base/parts/ipc/node/ipc'; -import { ISerializedFileMatch } from '../search'; import { IPatternInfo, ITextSearchPreviewOptions } from 'vs/platform/search/common/search'; import { SearchWorker } from './searchWorker'; import { Event } from 'vs/base/common/event'; +import { ISerializedFileMatch } from 'vs/workbench/services/search/node/search'; export interface ISearchWorkerSearchArgs { pattern: IPatternInfo; diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 2d3cf2bec6f..3ac1eec40a7 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -17,13 +17,14 @@ import * as strings from 'vs/base/common/strings'; import { TPromise } from 'vs/base/common/winjs.base'; import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; import { MAX_FILE_SIZE } from 'vs/platform/files/node/files'; -import { ICachedSearchStats, IFileSearchStats, IProgress } from 'vs/platform/search/common/search'; -import { Engine as FileSearchEngine, FileWalker } from 'vs/workbench/services/search/node/fileSearch'; +import { ICachedSearchStats, IFileSearchStats, IProgress, IRawTextQuery, ITextQuery, IRawQuery, IFileQuery, IFolderQuery } from 'vs/platform/search/common/search'; +import { Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; +import { LegacyTextSearchService } from 'vs/workbench/services/search/node/legacy/rawLegacyTextSearchService'; +import { IRawSearch } from 'vs/workbench/services/search/node/legacy/search'; import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; -import { Engine as TextSearchEngine } from 'vs/workbench/services/search/node/textSearch'; -import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/textSearchWorkerProvider'; -import { IFileSearchProgressItem, IRawFileMatch, IRawSearch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from './search'; -import { BatchedCollector } from 'vs/workbench/services/search/node/textSearchManager'; +import { IFileSearchProgressItem, IRawFileMatch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from './search'; +import { Schemas } from 'vs/base/common/network'; +import { URI, UriComponents } from 'vs/base/common/uri'; gracefulFs.gracefulify(fs); @@ -34,10 +35,9 @@ export class SearchService implements IRawSearchService { private static readonly BATCH_SIZE = 512; + private legacyTextSearchService = new LegacyTextSearchService(); private caches: { [cacheKey: string]: Cache; } = Object.create(null); - private textSearchWorkerProvider: TextSearchWorkerProvider; - public fileSearch(config: IRawSearch, batchSize = SearchService.BATCH_SIZE): Event { let promise: CancelablePromise; @@ -59,13 +59,16 @@ export class SearchService implements IRawSearchService { return emitter.event; } - public textSearch(config: IRawSearch): Event { + public textSearch(rawQuery: IRawTextQuery): Event { let promise: CancelablePromise; + const query = reviveQuery(rawQuery); const emitter = new Emitter({ onFirstListenerDidAdd: () => { promise = createCancelablePromise(token => { - return (config.useRipgrep ? this.ripgrepTextSearch(config, p => emitter.fire(p), token) : this.legacyTextSearch(config, p => emitter.fire(p), token)); + return (rawQuery.useRipgrep ? + this.ripgrepTextSearch(query, p => emitter.fire(p), token) : + this.legacyTextSearchService.textSearch(rawSearchQuery(query), p => emitter.fire(p), token)); }); promise.then( @@ -80,40 +83,11 @@ export class SearchService implements IRawSearchService { return emitter.event; } - private ripgrepTextSearch(config: IRawSearch, progressCallback: IProgressCallback, token: CancellationToken): Promise { - config.maxFilesize = MAX_FILE_SIZE; + private ripgrepTextSearch(config: ITextQuery, progressCallback: IProgressCallback, token: CancellationToken): Promise { + config.maxFileSize = MAX_FILE_SIZE; const engine = new TextSearchEngineAdapter(config); - return new Promise((c, e) => { - engine.search(token, progressCallback, progressCallback, (error, stats) => { - if (error) { - e(error); - } else { - c(stats); - } - }); - }); - } - - private legacyTextSearch(config: IRawSearch, progressCallback: IProgressCallback, token: CancellationToken): Promise { - if (!this.textSearchWorkerProvider) { - this.textSearchWorkerProvider = new TextSearchWorkerProvider(); - } - - let engine = new TextSearchEngine( - config, - new FileWalker({ - folderQueries: config.folderQueries, - extraFiles: config.extraFiles, - includePattern: config.includePattern, - excludePattern: config.excludePattern, - filePattern: config.filePattern, - useRipgrep: false, - maxFilesize: MAX_FILE_SIZE - }), - this.textSearchWorkerProvider); - - return this.doTextSearch(engine, progressCallback, SearchService.BATCH_SIZE, token); + return engine.search(token, progressCallback, progressCallback); } doFileSearch(EngineClass: { new(config: IRawSearch): ISearchEngine; }, config: IRawSearch, progressCallback: IProgressCallback, token?: CancellationToken, batchSize?: number): TPromise { @@ -363,32 +337,7 @@ export class SearchService implements IRawSearchService { })); } - private doTextSearch(engine: TextSearchEngine, progressCallback: IProgressCallback, batchSize: number, token: CancellationToken): Promise { - token.onCancellationRequested(() => engine.cancel()); - return new Promise((c, e) => { - // Use BatchedCollector to get new results to the frontend every 2s at least, until 50 results have been returned - const collector = new BatchedCollector(batchSize, progressCallback); - engine.search((matches) => { - const totalMatches = matches.reduce((acc, m) => acc + m.numMatches, 0); - collector.addItems(matches, totalMatches); - }, (progress) => { - progressCallback(progress); - }, (error, stats) => { - collector.flush(); - - if (error) { - e(error); - } else { - c({ - type: 'success', - limitHit: stats.limitHit, - stats: null - }); - } - }); - }); - } private doSearch(engine: ISearchEngine, progressCallback: IFileProgressCallback, batchSize: number, token?: CancellationToken): TPromise { return new TPromise((c, e) => { @@ -477,3 +426,61 @@ const FileMatchItemAccessor = new class implements IItemAccessor return match.relativePath; // e.g. some/path/to/file/myFile.txt } }; + +function reviveQuery(rawQuery: U): U extends IRawTextQuery ? ITextQuery : IFileQuery { + return { + ...rawQuery, // TODO + ...{ + folderQueries: rawQuery.folderQueries && rawQuery.folderQueries.map(reviveFolderQuery), + extraFileResources: rawQuery.extraFileResources && rawQuery.extraFileResources.map(components => URI.revive(components)) + } + }; +} + +function reviveFolderQuery(rawFolderQuery: IFolderQuery): IFolderQuery { + return { + ...rawFolderQuery, + folder: URI.revive(rawFolderQuery.folder) + }; +} + +/** + * Exported for tests + */ +export function rawSearchQuery(query: ITextQuery): IRawSearch { + let rawSearch: IRawSearch = { + folderQueries: [], + extraFiles: [], + excludePattern: query.excludePattern, + includePattern: query.includePattern, + maxResults: query.maxResults, + useRipgrep: query.useRipgrep, + disregardIgnoreFiles: query.folderQueries.some(fq => fq.disregardIgnoreFiles), + disregardGlobalIgnoreFiles: query.folderQueries.some(fq => fq.disregardGlobalIgnoreFiles), + ignoreSymlinks: query.folderQueries.some(fq => fq.ignoreSymlinks), + previewOptions: query.previewOptions + }; + + for (const q of query.folderQueries) { + rawSearch.folderQueries.push({ + excludePattern: q.excludePattern, + includePattern: q.includePattern, + fileEncoding: q.fileEncoding, + disregardIgnoreFiles: q.disregardIgnoreFiles, + disregardGlobalIgnoreFiles: q.disregardGlobalIgnoreFiles, + folder: q.folder.fsPath + }); + } + + if (query.extraFileResources) { + for (const r of query.extraFileResources) { + if (r.scheme === Schemas.file) { + rawSearch.extraFiles.push(r.fsPath); + } + } + } + + rawSearch.contentPattern = query.contentPattern; + + return rawSearch; +} diff --git a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts index 1930112bca5..4b91216a1ee 100644 --- a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts @@ -12,8 +12,8 @@ import * as paths from 'vs/base/common/paths'; import { isMacintosh as isMac } from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { rgPath } from 'vscode-ripgrep'; -import { IFolderSearch, IRawSearch } from './search'; import { anchorGlob } from 'vs/workbench/services/search/node/ripgrepSearchUtils'; +import { IRawSearch, IFolderSearch } from 'vs/workbench/services/search/node/legacy/search'; // If vscode-ripgrep is in an .asar file, then the binary is unpacked. const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked'); diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts index 888c11cb980..8248aef2083 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts @@ -12,6 +12,7 @@ import { URI } from 'vs/base/common/uri'; import * as vscode from 'vscode'; import { rgPath } from 'vscode-ripgrep'; import { anchorGlob, createTextSearchResult, IOutputChannel, Maybe, Range } from './ripgrepSearchUtils'; +import { IExtendedExtensionSearchOptions } from 'vs/platform/search/common/search'; // If vscode-ripgrep is in an .asar file, then the binary is unpacked. const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked'); @@ -117,26 +118,20 @@ export class RipgrepTextSearchEngine { export function rgErrorMsgForDisplay(msg: string): Maybe { const firstLine = msg.split('\n')[0].trim(); - if (startsWith(firstLine, 'Error parsing regex')) { - return firstLine; + if (startsWith(firstLine, 'regex parse error')) { + return 'Regex parse error'; } - if (startsWith(firstLine, 'error parsing glob') || - startsWith(firstLine, 'unsupported encoding')) { + let match = firstLine.match(/grep config error: unknown encoding: (.*)/); + if (match) { + return `Unknown encoding: ${match[1]}`; + } + + if (startsWith(firstLine, 'error parsing glob') || startsWith(firstLine, 'the literal')) { // Uppercase first letter return firstLine.charAt(0).toUpperCase() + firstLine.substr(1); } - if (firstLine === `Literal '\\n' not allowed.`) { - // I won't localize this because none of the Ripgrep error messages are localized - return `Literal '\\n' currently not supported`; - } - - if (startsWith(firstLine, 'Literal ')) { - // Other unsupported chars - return firstLine; - } - return undefined; } @@ -298,7 +293,7 @@ function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOpti const regexpStr = regexp.source.replace(/\\\//g, '/'); // RegExp.source arbitrarily returns escaped slashes. Search and destroy. args.push('--regexp', regexpStr); } else if (query.isRegExp) { - args.push('--regexp', fixRegexEndingPattern(query.pattern)); + args.push('--regexp', query.pattern); } else { searchPatternAfterDoubleDashes = query.pattern; args.push('--fixed-strings'); @@ -309,12 +304,19 @@ function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOpti args.push('--no-ignore-global'); } + // Match \r\n with $ + args.push('--crlf'); + args.push('--json'); if (query.isMultiline) { args.push('--multiline'); } + if ((options).usePCRE2) { + args.push('--pcre2'); + } + // Folder to search args.push('--'); @@ -382,11 +384,3 @@ function startsWithUTF8BOM(str: string): boolean { function stripUTF8BOM(str: string): string { return startsWithUTF8BOM(str) ? str.substr(1) : str; } - -export function fixRegexEndingPattern(pattern: string): string { - // Replace an unescaped $ at the end of the pattern with \r?$ - // Match $ preceeded by none or even number of literal \ - return pattern.match(/([^\\]|^)(\\\\)*\$$/) ? - pattern.replace(/\$$/, '\\r?$') : - pattern; -} \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/search.ts b/src/vs/workbench/services/search/node/search.ts index ca4103d568a..8b91c35fcb3 100644 --- a/src/vs/workbench/services/search/node/search.ts +++ b/src/vs/workbench/services/search/node/search.ts @@ -6,36 +6,9 @@ import { Event } from 'vs/base/common/event'; import * as glob from 'vs/base/common/glob'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IFileSearchStats, IPatternInfo, IProgress, ISearchEngineStats, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, ISearchQuery, IFolderQuery } from 'vs/platform/search/common/search'; +import { IFileSearchStats, IFolderQuery, IProgress, ISearchEngineStats, ISearchQuery, ITextSearchResult, ITextSearchStats, IRawTextQuery } from 'vs/platform/search/common/search'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; - -export interface IFolderSearch { - folder: string; - excludePattern?: glob.IExpression; - includePattern?: glob.IExpression; - fileEncoding?: string; - disregardIgnoreFiles?: boolean; - disregardGlobalIgnoreFiles?: boolean; -} - -export interface IRawSearch { - folderQueries: IFolderSearch[]; - ignoreSymlinks?: boolean; - extraFiles?: string[]; - filePattern?: string; - excludePattern?: glob.IExpression; - includePattern?: glob.IExpression; - contentPattern?: IPatternInfo; - maxResults?: number; - exists?: boolean; - sortByScore?: boolean; - cacheKey?: string; - maxFilesize?: number; - useRipgrep?: boolean; - disregardIgnoreFiles?: boolean; - previewOptions?: ITextSearchPreviewOptions; - disregardGlobalIgnoreFiles?: boolean; -} +import { IRawSearch } from 'vs/workbench/services/search/node/legacy/search'; export interface ITelemetryEvent { eventName: string; @@ -44,7 +17,7 @@ export interface ITelemetryEvent { export interface IRawSearchService { fileSearch(search: IRawSearch): Event; - textSearch(search: IRawSearch): Event; + textSearch(search: IRawTextQuery): Event; clearCache(cacheKey: string): TPromise; } diff --git a/src/vs/workbench/services/search/node/searchIpc.ts b/src/vs/workbench/services/search/node/searchIpc.ts index b1a5da50433..28770ed6811 100644 --- a/src/vs/workbench/services/search/node/searchIpc.ts +++ b/src/vs/workbench/services/search/node/searchIpc.ts @@ -6,11 +6,13 @@ import { Event } from 'vs/base/common/event'; import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel } from 'vs/base/parts/ipc/node/ipc'; -import { IRawSearch, IRawSearchService, ISerializedSearchComplete, ISerializedSearchProgressItem } from './search'; +import { IRawSearch } from 'vs/workbench/services/search/node/legacy/search'; +import { IRawSearchService, ISerializedSearchComplete, ISerializedSearchProgressItem } from './search'; +import { IRawTextQuery } from 'vs/platform/search/common/search'; export interface ISearchChannel extends IChannel { listen(event: 'fileSearch', search: IRawSearch): Event; - listen(event: 'textSearch', search: IRawSearch): Event; + listen(event: 'textSearch', search: IRawTextQuery): Event; call(command: 'clearCache', cacheKey: string): TPromise; call(command: string, arg: any): TPromise; } @@ -43,7 +45,7 @@ export class SearchChannelClient implements IRawSearchService { return this.channel.listen('fileSearch', search); } - textSearch(search: IRawSearch): Event { + textSearch(search: IRawTextQuery): Event { return this.channel.listen('textSearch', search); } diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index ece7230015c..472cf549fdb 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -9,11 +9,10 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { ResourceMap, values, keys } from 'vs/base/common/map'; +import { keys, ResourceMap, values } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import * as objects from 'vs/base/common/objects'; import { StopWatch } from 'vs/base/common/stopwatch'; -import * as strings from 'vs/base/common/strings'; import { URI as uri } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import * as pfs from 'vs/base/node/pfs'; @@ -25,12 +24,13 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDebugParams, IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; -import { FileMatch, ICachedSearchStats, IFileMatch, IFileSearchStats, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextSearchPreviewOptions, pathIncludedInQuery, QueryType, SearchProviderType, TextSearchResult } from 'vs/platform/search/common/search'; +import { FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextQuery, ITextSearchPreviewOptions, pathIncludedInQuery, QueryType, SearchProviderType, TextSearchResult } from 'vs/platform/search/common/search'; 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 { IRawSearch } from 'vs/workbench/services/search/node/legacy/search'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { IRawSearch, IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess } from './search'; +import { IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess } from './search'; import { ISearchChannel, SearchChannelClient } from './searchIpc'; export class SearchService extends Disposable implements ISearchService { @@ -72,15 +72,9 @@ export class SearchService extends Disposable implements ISearchService { }); } - public extendQuery(query: ISearchQuery): void { + public extendQuery(query: IFileQuery): void { const configuration = this.configurationService.getValue(); - // Configuration: Encoding - if (!query.fileEncoding) { - const fileEncoding = configuration && configuration.files && configuration.files.encoding; - query.fileEncoding = fileEncoding; - } - // Configuration: File Excludes if (!query.disregardExcludeSettings) { const fileExcludes = objects.deepClone(configuration && configuration.files && configuration.files.exclude); @@ -94,7 +88,7 @@ export class SearchService extends Disposable implements ISearchService { } } - public search(query: ISearchQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): TPromise { + public textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): TPromise { // Get local results from dirty/untitled const localResults = this.getLocalResults(query); @@ -120,6 +114,14 @@ export class SearchService extends Disposable implements ISearchService { } }; + return this.doSearch(query, token, onProviderProgress); + } + + public fileSearch(query: IFileQuery, token?: CancellationToken): TPromise { + return this.doSearch(query, token); + } + + private doSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): TPromise { const schemesInQuery = this.getSchemesInQuery(query); const providerActivations: TPromise[] = [TPromise.wrap(null)]; @@ -134,7 +136,7 @@ export class SearchService extends Disposable implements ISearchService { return TPromise.wrapError(canceled()); } - return this.searchWithProviders(query, onProviderProgress, token); + return this.searchWithProviders(query, onProgress, token); }) .then(completes => { completes = completes.filter(c => !!c); @@ -156,35 +158,6 @@ export class SearchService extends Disposable implements ISearchService { return TPromise.wrapError(errs[0]); }); - const searchP = providerPromise.then(value => { - const values = [value]; - - const result: ISearchComplete = { - limitHit: false, - results: [], - stats: undefined - }; - - // TODO@joh - // sorting, disjunct results - for (const value of values) { - if (!value) { - continue; - } - // TODO@joh individual stats/limit - result.stats = value.stats || result.stats; - result.limitHit = value.limitHit || result.limitHit; - - for (const match of value.results) { - if (!localResults.has(match.resource)) { - result.results.push(match); - } - } - } - - return result; - }); - return new TPromise((resolve, reject) => { if (token) { token.onCancellationRequested(() => { @@ -192,7 +165,7 @@ export class SearchService extends Disposable implements ISearchService { }); } - searchP.then(resolve, reject); + providerPromise.then(resolve, reject); }); } @@ -227,14 +200,16 @@ export class SearchService extends Disposable implements ISearchService { } else if (!provider) { throw new Error('No search provider registered for scheme: ' + scheme); } else { - const oneSchemeQuery = { + const oneSchemeQuery: ISearchQuery = { ...query, ...{ folderQueries: schemeFQs } }; - searchPs.push(provider.search(oneSchemeQuery, onProviderProgress, token)); + searchPs.push(query.type === QueryType.File ? + provider.fileSearch(oneSchemeQuery, token) : + provider.textSearch(oneSchemeQuery, onProviderProgress, token)); } }); @@ -249,7 +224,9 @@ export class SearchService extends Disposable implements ISearchService { extraFileResources: diskSearchExtraFileResources }; - searchPs.push(this.diskSearch.search(diskSearchQuery, onProviderProgress, token)); + searchPs.push(diskSearchQuery.type === QueryType.File ? + this.diskSearch.fileSearch(diskSearchQuery, token) : + this.diskSearch.textSearch(diskSearchQuery, onProviderProgress, token)); } return TPromise.join(searchPs).then(completes => { @@ -259,6 +236,17 @@ export class SearchService extends Disposable implements ISearchService { this.sendTelemetry(query, endToEndTime, complete); }); return completes; + }, errs => { + const endToEndTime = e2eSW.elapsed(); + this.logService.trace(`SearchService#search: ${endToEndTime}ms`); + errs = errs + .filter(e => !!e); + + errs.forEach(e => { + this.sendTelemetry(query, endToEndTime, null, e); + }); + + throw errs[0]; }); } @@ -275,20 +263,21 @@ export class SearchService extends Disposable implements ISearchService { return queries; } - private sendTelemetry(query: ISearchQuery, endToEndTime: number, complete: ISearchComplete): void { + private sendTelemetry(query: ISearchQuery, endToEndTime: number, complete?: ISearchComplete, err?: Error): void { const fileSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme === 'file'); const otherSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme !== 'file'); const scheme = fileSchemeOnly ? 'file' : otherSchemeOnly ? 'other' : 'mixed'; - if (query.type === QueryType.File && complete.stats) { + if (query.type === QueryType.File && complete && complete.stats) { const fileSearchStats = complete.stats as IFileSearchStats; if (fileSearchStats.fromCache) { const cacheStats: ICachedSearchStats = fileSearchStats.detailStats as ICachedSearchStats; /* __GDPR__ "cachedSearchComplete" : { + "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, @@ -302,6 +291,7 @@ export class SearchService extends Disposable implements ISearchService { } */ this.telemetryService.publicLog('cachedSearchComplete', { + reason: query._reason, resultCount: fileSearchStats.resultCount, workspaceFolderCount: query.folderQueries.length, type: fileSearchStats.type, @@ -318,6 +308,7 @@ export class SearchService extends Disposable implements ISearchService { /* __GDPR__ "searchComplete" : { + "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, @@ -329,10 +320,12 @@ export class SearchService extends Disposable implements ISearchService { "filesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "cmdTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "cmdResultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "useRipgrep" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } */ this.telemetryService.publicLog('searchComplete', { + reason: query._reason, resultCount: fileSearchStats.resultCount, workspaceFolderCount: query.folderQueries.length, type: fileSearchStats.type, @@ -344,13 +337,44 @@ export class SearchService extends Disposable implements ISearchService { filesWalked: searchEngineStats.filesWalked, cmdTime: searchEngineStats.cmdTime, cmdResultCount: searchEngineStats.cmdResultCount, - scheme + scheme, + useRipgrep: query.useRipgrep }); } + } else if (query.type === QueryType.Text) { + let errorType: string; + if (err) { + errorType = err.message.indexOf('Regex parse error') >= 0 ? 'regex' : + err.message.indexOf('Unknown encoding') >= 0 ? 'encoding' : + err.message.indexOf('Error parsing glob') >= 0 ? 'glob' : + err.message.indexOf('The literal') >= 0 ? 'literal' : + 'other'; + } + + /* __GDPR__ + "textSearchComplete" : { + "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "error" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "useRipgrep" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "usePCRE2" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } + */ + this.telemetryService.publicLog('textSearchComplete', { + reason: query._reason, + workspaceFolderCount: query.folderQueries.length, + endToEndTime: endToEndTime, + scheme, + error: errorType, + useRipgrep: query.useRipgrep, + usePCRE2: query.usePCRE2 + }); } } - private getLocalResults(query: ISearchQuery): ResourceMap { + private getLocalResults(query: ITextQuery): ResourceMap { const localResults = new ResourceMap(); if (query.type === QueryType.Text) { @@ -402,18 +426,7 @@ export class SearchService extends Disposable implements ISearchService { return localResults; } - private matches(resource: uri, query: ISearchQuery): boolean { - // file pattern - if (query.filePattern) { - if (resource.scheme !== Schemas.file) { - return false; // if we match on file pattern, we have to ignore non file resources - } - - if (!strings.fuzzyContains(resource.fsPath, strings.stripWildcards(query.filePattern).toLowerCase())) { - return false; - } - } - + private matches(resource: uri, query: ITextQuery): boolean { // includes if (query.includePattern) { if (resource.scheme !== Schemas.file) { @@ -436,7 +449,6 @@ export class SearchService extends Disposable implements ISearchService { } export class DiskSearch implements ISearchResultProvider { - private raw: IRawSearchService; constructor(verboseLogging: boolean, timeout: number = 60 * 60 * 1000, searchDebug?: IDebugParams) { @@ -472,7 +484,22 @@ export class DiskSearch implements ISearchResultProvider { this.raw = new SearchChannelClient(channel); } - public search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): TPromise { + textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): TPromise { + const folderQueries = query.folderQueries || []; + return TPromise.join(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) + .then(exists => { + if (token && token.isCancellationRequested) { + throw canceled(); + } + + query.folderQueries = folderQueries.filter((q, index) => exists[index]); + const event: Event = this.raw.textSearch(query); + + return DiskSearch.collectResultsFromEvent(event, onProgress, token); + }); + } + + fileSearch(query: IFileQuery, token?: CancellationToken): TPromise { const folderQueries = query.folderQueries || []; return TPromise.join(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) .then(exists => { @@ -484,17 +511,13 @@ export class DiskSearch implements ISearchResultProvider { const rawSearch = this.rawSearchQuery(query, existingFolders); let event: Event; - if (query.type === QueryType.File) { - event = this.raw.fileSearch(rawSearch); - } else { - event = this.raw.textSearch(rawSearch); - } + event = this.raw.fileSearch(rawSearch); - return DiskSearch.collectResultsFromEvent(event, onProgress, token); + return DiskSearch.collectResultsFromEvent(event, null, token); }); } - private rawSearchQuery(query: ISearchQuery, existingFolders: IFolderQuery[]) { + private rawSearchQuery(query: IFileQuery, existingFolders: IFolderQuery[]) { let rawSearch: IRawSearch = { folderQueries: [], extraFiles: [], @@ -505,11 +528,7 @@ export class DiskSearch implements ISearchResultProvider { exists: query.exists, sortByScore: query.sortByScore, cacheKey: query.cacheKey, - useRipgrep: query.useRipgrep, - disregardIgnoreFiles: query.disregardIgnoreFiles, - disregardGlobalIgnoreFiles: query.disregardGlobalIgnoreFiles, - ignoreSymlinks: query.ignoreSymlinks, - previewOptions: query.previewOptions + useRipgrep: query.useRipgrep }; for (const q of existingFolders) { @@ -531,10 +550,6 @@ export class DiskSearch implements ISearchResultProvider { } } - if (query.type === QueryType.Text) { - rawSearch.contentPattern = query.contentPattern; - } - return rawSearch; } diff --git a/src/vs/workbench/services/search/node/textSearchAdapter.ts b/src/vs/workbench/services/search/node/textSearchAdapter.ts index 20dbb3c90c5..6759cd6e8dd 100644 --- a/src/vs/workbench/services/search/node/textSearchAdapter.ts +++ b/src/vs/workbench/services/search/node/textSearchAdapter.ts @@ -4,71 +4,45 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; -import { URI } from 'vs/base/common/uri'; import * as extfs from 'vs/base/node/extfs'; -import { IFolderQuery, IProgress, ISearchQuery, ITextSearchStats, QueryType, IFileMatch } from 'vs/platform/search/common/search'; +import { IFileMatch, IProgress, ITextQuery, ITextSearchStats } from 'vs/platform/search/common/search'; import { RipgrepTextSearchEngine } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine'; import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager'; -import { IRawSearch, ISerializedFileMatch, ISerializedSearchSuccess } from './search'; +import { ISerializedFileMatch, ISerializedSearchSuccess } from './search'; export class TextSearchEngineAdapter { - constructor(private config: IRawSearch) { + constructor(private query: ITextQuery) { } - // TODO@Rob - make promise-based once the old search is gone, and I don't need them to have matching interfaces anymore - search(token: CancellationToken, onResult: (matches: ISerializedFileMatch[]) => void, onMessage: (message: IProgress) => void, done: (error: Error, complete: ISerializedSearchSuccess) => void): void { - if (!this.config.folderQueries.length && !this.config.extraFiles.length) { - done(null, { + search(token: CancellationToken, onResult: (matches: ISerializedFileMatch[]) => void, onMessage: (message: IProgress) => void): Promise { + if (!this.query.folderQueries.length && !this.query.extraFileResources.length) { + return Promise.resolve({ type: 'success', limitHit: false, stats: { type: 'searchProcess' } }); - return; } - const query: ISearchQuery = { - type: QueryType.Text, - cacheKey: this.config.cacheKey, - contentPattern: this.config.contentPattern, - - excludePattern: this.config.excludePattern, - includePattern: this.config.includePattern, - extraFileResources: this.config.extraFiles && this.config.extraFiles.map(f => URI.file(f)), - fileEncoding: this.config.folderQueries[0].fileEncoding, // ? - maxResults: this.config.maxResults, - exists: this.config.exists, - sortByScore: this.config.sortByScore, - disregardIgnoreFiles: this.config.disregardIgnoreFiles, - disregardGlobalIgnoreFiles: this.config.disregardGlobalIgnoreFiles, - ignoreSymlinks: this.config.ignoreSymlinks, - maxFileSize: this.config.maxFilesize, - previewOptions: this.config.previewOptions - }; - query.folderQueries = this.config.folderQueries.map(fq => { - disregardGlobalIgnoreFiles: fq.disregardGlobalIgnoreFiles, - disregardIgnoreFiles: fq.disregardIgnoreFiles, - excludePattern: fq.excludePattern, - fileEncoding: fq.fileEncoding, - folder: URI.file(fq.folder), - includePattern: fq.includePattern - }); - const pretendOutputChannel = { appendLine(msg) { onMessage({ message: msg }); } }; - const textSearchManager = new TextSearchManager(this.config.contentPattern, query, new RipgrepTextSearchEngine(pretendOutputChannel), extfs); - textSearchManager - .search( - matches => { - onResult(matches.map(fileMatchToSerialized)); - }, - token) - .then(() => done(null, { limitHit: false, stats: null, type: 'success' })); + const textSearchManager = new TextSearchManager(this.query, new RipgrepTextSearchEngine(pretendOutputChannel), extfs); + return new Promise((resolve, reject) => { + return textSearchManager + .search( + matches => { + onResult(matches.map(fileMatchToSerialized)); + }, + token) + .then( + () => resolve({ limitHit: false, stats: null, type: 'success' }), + reject); + }); } } diff --git a/src/vs/workbench/services/search/node/textSearchManager.ts b/src/vs/workbench/services/search/node/textSearchManager.ts index 828819afaad..1064ddec943 100644 --- a/src/vs/workbench/services/search/node/textSearchManager.ts +++ b/src/vs/workbench/services/search/node/textSearchManager.ts @@ -11,9 +11,9 @@ import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import * as extfs from 'vs/base/node/extfs'; -import { IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ISearchQuery, ITextSearchResult } from 'vs/platform/search/common/search'; -import * as vscode from 'vscode'; +import { IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ITextQuery, ITextSearchResult, IExtendedExtensionSearchOptions } from 'vs/platform/search/common/search'; import { QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/node/search'; +import * as vscode from 'vscode'; export class TextSearchManager { @@ -22,11 +22,11 @@ export class TextSearchManager { private isLimitHit: boolean; private resultCount = 0; - constructor(private pattern: IPatternInfo, private config: ISearchQuery, private provider: vscode.TextSearchProvider, private _extfs: typeof extfs) { + constructor(private query: ITextQuery, private provider: vscode.TextSearchProvider, private _extfs: typeof extfs) { } public search(onProgress: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise { - const folderQueries = this.config.folderQueries; + const folderQueries = this.query.folderQueries; const tokenSource = new CancellationTokenSource(); token.onCancellationRequested(() => tokenSource.cancel()); @@ -39,7 +39,7 @@ export class TextSearchManager { return; } - if (this.resultCount >= this.config.maxResults) { + if (this.resultCount >= this.query.maxResults) { this.isLimitHit = true; isCanceled = true; tokenSource.cancel(); @@ -77,8 +77,8 @@ export class TextSearchManager { } private searchInFolder(folderQuery: IFolderQuery, onResult: (result: vscode.TextSearchResult) => void, token: CancellationToken): TPromise { - const queryTester = new QueryGlobTester(this.config, folderQuery); - const testingPs = []; + const queryTester = new QueryGlobTester(this.query, folderQuery); + const testingPs: TPromise[] = []; const progress = { report: (result: vscode.TextSearchResult) => { const hasSibling = folderQuery.folder.scheme === 'file' && glob.hasSiblingPromiseFn(() => { @@ -98,7 +98,7 @@ export class TextSearchManager { const searchOptions = this.getSearchOptionsForFolder(folderQuery); return new TPromise(resolve => process.nextTick(resolve)) - .then(() => this.provider.provideTextSearchResults(patternInfoToQuery(this.pattern), searchOptions, progress, token)) + .then(() => this.provider.provideTextSearchResults(patternInfoToQuery(this.query.contentPattern), searchOptions, progress, token)) .then(result => { return TPromise.join(testingPs) .then(() => result); @@ -118,21 +118,23 @@ export class TextSearchManager { } private getSearchOptionsForFolder(fq: IFolderQuery): vscode.TextSearchOptions { - const includes = resolvePatternsForProvider(this.config.includePattern, fq.includePattern); - const excludes = resolvePatternsForProvider(this.config.excludePattern, fq.excludePattern); + const includes = resolvePatternsForProvider(this.query.includePattern, fq.includePattern); + const excludes = resolvePatternsForProvider(this.query.excludePattern, fq.excludePattern); - return { + const options = { folder: URI.from(fq.folder), excludes, includes, - useIgnoreFiles: !this.config.disregardIgnoreFiles, - useGlobalIgnoreFiles: !this.config.disregardGlobalIgnoreFiles, - followSymlinks: !this.config.ignoreSymlinks, - encoding: this.config.fileEncoding, - maxFileSize: this.config.maxFileSize, - maxResults: this.config.maxResults, - previewOptions: this.config.previewOptions + useIgnoreFiles: !fq.disregardIgnoreFiles, + useGlobalIgnoreFiles: !fq.disregardGlobalIgnoreFiles, + followSymlinks: !fq.ignoreSymlinks, + encoding: fq.fileEncoding, + maxFileSize: this.query.maxFileSize, + maxResults: this.query.maxResults, + previewOptions: this.query.previewOptions }; + (options).usePCRE2 = this.query.usePCRE2; + return options; } } diff --git a/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts b/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts deleted file mode 100644 index 666116d9378..00000000000 --- a/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { fixRegexEndingPattern } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine'; - -suite('RipgrepTextSearch - etc', () => { - test('fixRegexEndingPattern', () => { - function testFixRegexEndingPattern([input, expectedResult]: string[]): void { - assert.equal(fixRegexEndingPattern(input), expectedResult); - } - - [ - ['foo', 'foo'], - ['', ''], - ['^foo.*bar\\s+', '^foo.*bar\\s+'], - ['foo$', 'foo\\r?$'], - ['$', '\\r?$'], - ['foo\\$', 'foo\\$'], - ['foo\\\\$', 'foo\\\\\\r?$'], - ].forEach(testFixRegexEndingPattern); - }); - - test('fixRegexEndingPattern', () => { - function testFixRegexEndingPattern([input, expectedResult]: string[]): void { - assert.equal(fixRegexEndingPattern(input), expectedResult); - } - - [ - ['foo', 'foo'], - ['', ''], - ['^foo.*bar\\s+', '^foo.*bar\\s+'], - ['foo$', 'foo\\r?$'], - ['$', '\\r?$'], - ['foo\\$', 'foo\\$'], - ['foo\\\\$', 'foo\\\\\\r?$'], - ].forEach(testFixRegexEndingPattern); - }); -}); \ No newline at end of file diff --git a/src/vs/workbench/services/search/test/node/search.test.ts b/src/vs/workbench/services/search/test/node/search.test.ts index 3faddb9173d..1f89b9c81e2 100644 --- a/src/vs/workbench/services/search/test/node/search.test.ts +++ b/src/vs/workbench/services/search/test/node/search.test.ts @@ -10,8 +10,9 @@ import { join, normalize } from 'vs/base/common/paths'; import * as platform from 'vs/base/common/platform'; import { FileWalker, Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; -import { IRawFileMatch, IFolderSearch } from 'vs/workbench/services/search/node/search'; import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { IFolderSearch } from 'vs/workbench/services/search/node/legacy/search'; +import { IRawFileMatch } from 'vs/workbench/services/search/node/search'; const TEST_FIXTURES = path.normalize(getPathFromAmdModule(require, './fixtures')); const EXAMPLES_FIXTURES = path.join(TEST_FIXTURES, 'examples'); diff --git a/src/vs/workbench/services/search/test/node/searchService.test.ts b/src/vs/workbench/services/search/test/node/searchService.test.ts index be347aa6c24..8b98983ca3c 100644 --- a/src/vs/workbench/services/search/test/node/searchService.test.ts +++ b/src/vs/workbench/services/search/test/node/searchService.test.ts @@ -10,8 +10,9 @@ import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async import { Emitter, Event } from 'vs/base/common/event'; import { IProgress, ISearchEngineStats, IFileSearchStats } from 'vs/platform/search/common/search'; import { SearchService as RawSearchService } from 'vs/workbench/services/search/node/rawSearchService'; -import { IFolderSearch, IRawFileMatch, IRawSearch, ISearchEngine, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess, ISearchEngineSuccess } from 'vs/workbench/services/search/node/search'; import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; +import { IFolderSearch, IRawSearch } from 'vs/workbench/services/search/node/legacy/search'; +import { ISearchEngine, IRawFileMatch, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchProgressItem, ISerializedSearchComplete, ISerializedSearchSuccess } from 'vs/workbench/services/search/node/search'; const TEST_FOLDER_QUERIES = [ { folder: path.normalize('/some/where') } diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts index 9e111e6d1c0..1d151a4bb6a 100644 --- a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -3,18 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; import * as assert from 'assert'; - -import * as glob from 'vs/base/common/glob'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { FileWalker } from 'vs/workbench/services/search/node/fileSearch'; -import { ISerializedFileMatch, IRawSearch, IFolderSearch } from 'vs/workbench/services/search/node/search'; -import { Engine as TextSearchEngine } from 'vs/workbench/services/search/node/textSearch'; -import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; -import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/textSearchWorkerProvider'; +import * as path from 'path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import * as glob from 'vs/base/common/glob'; +import { URI } from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IFolderQuery, ITextQuery, QueryType } from 'vs/platform/search/common/search'; +import { FileWalker } from 'vs/workbench/services/search/node/fileSearch'; +import { IRawSearch } from 'vs/workbench/services/search/node/legacy/search'; +import { Engine as TextSearchEngine } from 'vs/workbench/services/search/node/legacy/textSearch'; +import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/legacy/textSearchWorkerProvider'; +import { rawSearchQuery } from 'vs/workbench/services/search/node/rawSearchService'; +import { ISerializedFileMatch } from 'vs/workbench/services/search/node/search'; +import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; function countAll(matches: ISerializedFileMatch[]): number { return matches.reduce((acc, m) => acc + m.numMatches, 0); @@ -23,14 +26,14 @@ function countAll(matches: ISerializedFileMatch[]): number { const TEST_FIXTURES = path.normalize(getPathFromAmdModule(require, './fixtures')); const EXAMPLES_FIXTURES = path.join(TEST_FIXTURES, 'examples'); const MORE_FIXTURES = path.join(TEST_FIXTURES, 'more'); -const TEST_ROOT_FOLDER: IFolderSearch = { folder: TEST_FIXTURES }; -const ROOT_FOLDER_QUERY: IFolderSearch[] = [ +const TEST_ROOT_FOLDER: IFolderQuery = { folder: URI.file(TEST_FIXTURES) }; +const ROOT_FOLDER_QUERY: IFolderQuery[] = [ TEST_ROOT_FOLDER ]; -const MULTIROOT_QUERIES: IFolderSearch[] = [ - { folder: EXAMPLES_FIXTURES }, - { folder: MORE_FIXTURES } +const MULTIROOT_QUERIES: IFolderQuery[] = [ + { folder: URI.file(EXAMPLES_FIXTURES) }, + { folder: URI.file(MORE_FIXTURES) } ]; const textSearchWorkerProvider = new TextSearchWorkerProvider(); @@ -61,42 +64,36 @@ function doLegacySearchTest(config: IRawSearch, expectedResultCount: number | Fu }); } -function doRipgrepSearchTest(config: IRawSearch, expectedResultCount: number | Function): TPromise { - return new TPromise((resolve, reject) => { - let engine = new TextSearchEngineAdapter(config); +function doRipgrepSearchTest(query: ITextQuery, expectedResultCount: number | Function): TPromise { + let engine = new TextSearchEngineAdapter(query); - let c = 0; - engine.search(new CancellationTokenSource().token, (results) => { - if (results) { - c += results.reduce((acc, cur) => acc + cur.numMatches, 0); + let c = 0; + return engine.search(new CancellationTokenSource().token, (results) => { + if (results) { + c += results.reduce((acc, cur) => acc + cur.numMatches, 0); + } + }, () => { }).then( + () => { + if (typeof expectedResultCount === 'function') { + assert(expectedResultCount(c)); + } else { + assert.equal(c, expectedResultCount, `rg ${c} !== ${expectedResultCount}`); } - }, () => { }, (error) => { - try { - assert.ok(!error); - if (typeof expectedResultCount === 'function') { - assert(expectedResultCount(c)); - } else { - assert.equal(c, expectedResultCount, `rg ${c} !== ${expectedResultCount}`); - } - } catch (e) { - reject(e); - } - - resolve(undefined); }); - }); } -function doSearchTest(config: IRawSearch, expectedResultCount: number) { - return doLegacySearchTest(config, expectedResultCount) - .then(() => doRipgrepSearchTest(config, expectedResultCount)); +function doSearchTest(query: ITextQuery, expectedResultCount: number) { + const legacyQuery = rawSearchQuery(query); + return doLegacySearchTest(legacyQuery, expectedResultCount) + .then(() => doRipgrepSearchTest(query, expectedResultCount)); } suite('Search-integration', function () { this.timeout(1000 * 60); // increase timeout for this suite test('Text: GameOfLife', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'GameOfLife' }, }; @@ -105,7 +102,8 @@ suite('Search-integration', function () { }); test('Text: GameOfLife (RegExp)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'Game.?fL\\w?fe', isRegExp: true } }; @@ -113,8 +111,20 @@ suite('Search-integration', function () { return doSearchTest(config, 4); }); + test('Text: GameOfLife (PCRE2 RegExp)', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + usePCRE2: true, + contentPattern: { pattern: 'Life(?!P)', isRegExp: true } + }; + + return doSearchTest(config, 8); + }); + test('Text: GameOfLife (RegExp to EOL)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'GameOfLife.*', isRegExp: true } }; @@ -123,7 +133,8 @@ suite('Search-integration', function () { }); test('Text: GameOfLife (Word Match, Case Sensitive)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'GameOfLife', isWordMatch: true, isCaseSensitive: true } }; @@ -132,7 +143,8 @@ suite('Search-integration', function () { }); test('Text: GameOfLife (Word Match, Spaces)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: ' GameOfLife ', isWordMatch: true } }; @@ -141,7 +153,8 @@ suite('Search-integration', function () { }); test('Text: GameOfLife (Word Match, Punctuation and Spaces)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: ', as =', isWordMatch: true } }; @@ -150,7 +163,8 @@ suite('Search-integration', function () { }); test('Text: Helvetica (UTF 16)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'Helvetica' } }; @@ -159,7 +173,8 @@ suite('Search-integration', function () { }); test('Text: e', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'e' } }; @@ -232,7 +247,8 @@ suite('Search-integration', function () { test('Text: a (capped)', () => { const maxResults = 520; - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'a' }, maxResults @@ -240,12 +256,13 @@ suite('Search-integration', function () { // (Legacy) search can go over the maxResults because it doesn't trim the results from its worker processes to the exact max size. // But the worst-case scenario should be 2*max-1 - return doLegacySearchTest(config, count => count < maxResults * 2) + return doLegacySearchTest(rawSearchQuery(config), count => count < maxResults * 2) .then(() => doRipgrepSearchTest(config, maxResults)); }); test('Text: a (no results)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'ahsogehtdas' } }; @@ -254,7 +271,8 @@ suite('Search-integration', function () { }); test('Text: -size', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: '-size' } }; @@ -263,7 +281,8 @@ suite('Search-integration', function () { }); test('Multiroot: Conway', () => { - const config: IRawSearch = { + const config: ITextQuery = { + type: QueryType.Text, folderQueries: MULTIROOT_QUERIES, contentPattern: { pattern: 'conway' } }; @@ -272,7 +291,8 @@ suite('Search-integration', function () { }); test('Multiroot: e with partial global exclude', () => { - const config: IRawSearch = { + const config: ITextQuery = { + type: QueryType.Text, folderQueries: MULTIROOT_QUERIES, contentPattern: { pattern: 'e' }, excludePattern: makeExpression('**/*.txt') @@ -282,7 +302,8 @@ suite('Search-integration', function () { }); test('Multiroot: e with global excludes', () => { - const config: IRawSearch = { + const config: ITextQuery = { + type: QueryType.Text, folderQueries: MULTIROOT_QUERIES, contentPattern: { pattern: 'e' }, excludePattern: makeExpression('**/*.txt', '**/*.js') @@ -292,16 +313,83 @@ suite('Search-integration', function () { }); test('Multiroot: e with folder exclude', () => { - const config: IRawSearch = { + const config: ITextQuery = { + type: QueryType.Text, folderQueries: [ - { folder: EXAMPLES_FIXTURES, excludePattern: makeExpression('**/e*.js') }, - { folder: MORE_FIXTURES } + { folder: URI.file(EXAMPLES_FIXTURES), excludePattern: makeExpression('**/e*.js') }, + { folder: URI.file(MORE_FIXTURES) } ], contentPattern: { pattern: 'e' } }; return doSearchTest(config, 286); }); + + suite('error messages', () => { + test('invalid encoding', () => { + const config = { + type: QueryType.Text, + folderQueries: [ + { + ...TEST_ROOT_FOLDER, + fileEncoding: 'invalidEncoding' + } + ], + contentPattern: { pattern: 'test' }, + }; + + return doRipgrepSearchTest(config, 0).then(() => { + throw new Error('expected fail'); + }, err => { + assert.equal(err.message, 'Unknown encoding: invalidEncoding'); + }); + }); + + test('invalid regex', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: ')', isRegExp: true }, + }; + + return doRipgrepSearchTest(config, 0).then(() => { + throw new Error('expected fail'); + }, err => { + assert.equal(err.message, 'Regex parse error'); + }); + }); + + test('invalid glob', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'foo' }, + includePattern: { + '***': true + } + }; + + return doRipgrepSearchTest(config, 0).then(() => { + throw new Error('expected fail'); + }, err => { + assert.equal(err.message, 'Error parsing glob \'***\': invalid use of **; must be one path component'); + }); + }); + + test('invalid literal', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'foo\nbar', isRegExp: true } + }; + + return doRipgrepSearchTest(config, 0).then(() => { + throw new Error('expected fail'); + }, err => { + assert.equal(err.message, 'The literal \'"\\n"\' is not allowed in a regex'); + }); + }); + }); }); function makeExpression(...patterns: string[]): glob.IExpression { diff --git a/src/vs/workbench/services/viewlet/browser/viewlet.ts b/src/vs/workbench/services/viewlet/browser/viewlet.ts index 7ae5e2acc4f..88e0be01c41 100644 --- a/src/vs/workbench/services/viewlet/browser/viewlet.ts +++ b/src/vs/workbench/services/viewlet/browser/viewlet.ts @@ -22,7 +22,7 @@ export interface IViewletService { /** * Opens a viewlet with the given identifier and pass keyboard focus to it if specified. */ - openViewlet(id: string, focus?: boolean): Promise; + openViewlet(id: string, focus?: boolean): Thenable; /** * Returns the current active viewlet or null if none. diff --git a/src/vs/workbench/services/viewlet/browser/viewletService.ts b/src/vs/workbench/services/viewlet/browser/viewletService.ts index cde5c0470bf..8ed4196a2d9 100644 --- a/src/vs/workbench/services/viewlet/browser/viewletService.ts +++ b/src/vs/workbench/services/viewlet/browser/viewletService.ts @@ -68,17 +68,17 @@ export class ViewletService extends Disposable implements IViewletService { } } - openViewlet(id: string, focus?: boolean): Promise { + openViewlet(id: string, focus?: boolean): Thenable { if (this.getViewlet(id)) { - return Promise.resolve(this.sidebarPart.openViewlet(id, focus)); + return this.sidebarPart.openViewlet(id, focus); } - return Promise.resolve(this.extensionService.whenInstalledExtensionsRegistered() + return this.extensionService.whenInstalledExtensionsRegistered() .then(() => { if (this.getViewlet(id)) { return this.sidebarPart.openViewlet(id, focus); } return null; - })); + }); } getActiveViewlet(): IViewlet { diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index 95ef6e612b1..6f1e04113c8 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -8,7 +8,6 @@ import { URI } from 'vs/base/common/uri'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { MainThreadConfigurationShape, IConfigurationInitData } from 'vs/workbench/api/node/extHost.protocol'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { TestRPCProtocol } from './testRPCProtocol'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; @@ -22,9 +21,9 @@ suite('ExtHostConfiguration', function () { class RecordingShape extends mock() { lastArgs: [ConfigurationTarget, string, any]; - $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): TPromise { + $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): Promise { this.lastArgs = [target, key, value]; - return TPromise.as(void 0); + return Promise.resolve(void 0); } } @@ -576,8 +575,8 @@ suite('ExtHostConfiguration', function () { test('update/error-state not OK', function () { const shape = new class extends mock() { - $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): TPromise { - return TPromise.wrapError(new Error('Unknown Key')); // something !== OK + $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): Promise { + return Promise.reject(new Error('Unknown Key')); // something !== OK } }; diff --git a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts index fc8f8e17a6a..da34e6e3cf9 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -3,21 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { dispose } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as extfs from 'vs/base/node/extfs'; -import { IFileMatch, IPatternInfo, IRawFileMatch2, IRawSearchQuery, ISearchCompleteStats, ISearchQuery, QueryType } from 'vs/platform/search/common/search'; +import { IFileMatch, IFileQuery, IPatternInfo, IRawFileMatch2, ISearchCompleteStats, ISearchQuery, ITextQuery, QueryType } from 'vs/platform/search/common/search'; import { MainContext, MainThreadSearchShape } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { ExtHostSearch } from 'vs/workbench/api/node/extHostSearch'; import { Range } from 'vs/workbench/api/node/extHostTypes'; import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol'; -import * as vscode from 'vscode'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { TestLogService } from 'vs/workbench/test/workbenchTestServices'; -import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import * as vscode from 'vscode'; let rpcProtocol: TestRPCProtocol; let extHostSearch: ExtHostSearch; @@ -78,13 +77,13 @@ suite('ExtHostSearch', () => { await rpcProtocol.sync(); } - async function runFileSearch(query: IRawSearchQuery, cancel = false): Promise<{ results: URI[]; stats: ISearchCompleteStats }> { + async function runFileSearch(query: IFileQuery, cancel = false): Promise<{ results: URI[]; stats: ISearchCompleteStats }> { let stats: ISearchCompleteStats; try { const cancellation = new CancellationTokenSource(); const p = extHostSearch.$provideFileSearchResults(mockMainThreadSearch.lastHandle, 0, query, cancellation.token); if (cancel) { - await new TPromise(resolve => process.nextTick(resolve)); + await new Promise(resolve => process.nextTick(resolve)); cancellation.cancel(); } @@ -103,13 +102,13 @@ suite('ExtHostSearch', () => { }; } - async function runTextSearch(pattern: IPatternInfo, query: IRawSearchQuery, cancel = false): Promise<{ results: IFileMatch[], stats: ISearchCompleteStats }> { + async function runTextSearch(query: ITextQuery, cancel = false): Promise<{ results: IFileMatch[], stats: ISearchCompleteStats }> { let stats: ISearchCompleteStats; try { const cancellation = new CancellationTokenSource(); - const p = extHostSearch.$provideTextSearchResults(mockMainThreadSearch.lastHandle, 0, pattern, query, cancellation.token); + const p = extHostSearch.$provideTextSearchResults(mockMainThreadSearch.lastHandle, 0, query, cancellation.token); if (cancel) { - await new TPromise(resolve => process.nextTick(resolve)); + await new Promise(resolve => process.nextTick(resolve)); cancellation.cancel(); } @@ -157,7 +156,7 @@ suite('ExtHostSearch', () => { suite('File:', () => { - function getSimpleQuery(filePattern = ''): ISearchQuery { + function getSimpleQuery(filePattern = ''): IFileQuery { return { type: QueryType.File, @@ -179,7 +178,7 @@ suite('ExtHostSearch', () => { test('no results', async () => { await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { - return TPromise.wrap(null); + return Promise.resolve(null); } }); @@ -197,7 +196,7 @@ suite('ExtHostSearch', () => { await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { - return TPromise.wrap(reportedResults); + return Promise.resolve(reportedResults); } }); @@ -211,7 +210,7 @@ suite('ExtHostSearch', () => { let cancelRequested = false; await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { token.onCancellationRequested(() => { cancelRequested = true; @@ -245,7 +244,7 @@ suite('ExtHostSearch', () => { await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { assert(options.excludes.length === 2 && options.includes.length === 2, 'Missing global include/excludes'); - return TPromise.wrap(null); + return Promise.resolve(null); } }); @@ -281,7 +280,7 @@ suite('ExtHostSearch', () => { assert.deepEqual(options.excludes.sort(), ['*.js']); } - return TPromise.wrap(null); + return Promise.resolve(null); } }); @@ -318,7 +317,7 @@ suite('ExtHostSearch', () => { assert.deepEqual(options.includes.sort(), ['*.jsx', '*.ts']); assert.deepEqual(options.excludes.sort(), []); - return TPromise.wrap(null); + return Promise.resolve(null); } }); @@ -358,7 +357,7 @@ suite('ExtHostSearch', () => { await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { - return TPromise.wrap(reportedResults + return Promise.resolve(reportedResults .map(relativePath => joinPath(options.folder, relativePath))); } }); @@ -404,7 +403,7 @@ suite('ExtHostSearch', () => { ].map(relativePath => joinPath(rootFolderB, relativePath)); } - return TPromise.wrap(reportedResults); + return Promise.resolve(reportedResults); } }); @@ -461,7 +460,7 @@ suite('ExtHostSearch', () => { provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); - return TPromise.wrap(reportedResults); + return Promise.resolve(reportedResults); } }); @@ -497,7 +496,7 @@ suite('ExtHostSearch', () => { provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); - return TPromise.wrap(reportedResults); + return Promise.resolve(reportedResults); } }); @@ -532,7 +531,7 @@ suite('ExtHostSearch', () => { provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); - return TPromise.wrap(reportedResults); + return Promise.resolve(reportedResults); } }); @@ -563,7 +562,7 @@ suite('ExtHostSearch', () => { token.onCancellationRequested(() => cancels++); // Provice results async so it has a chance to invoke every provider - await new TPromise(r => process.nextTick(r)); + await new Promise(r => process.nextTick(r)); return [ 'file1.ts', 'file2.ts', @@ -603,7 +602,7 @@ suite('ExtHostSearch', () => { await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { - return TPromise.wrap(reportedResults); + return Promise.resolve(reportedResults); } }, fancyScheme); @@ -639,9 +638,10 @@ suite('ExtHostSearch', () => { }; } - function getSimpleQuery(): ISearchQuery { + function getSimpleQuery(queryText: string): ITextQuery { return { type: QueryType.Text, + contentPattern: getPattern(queryText), folderQueries: [ { folder: rootFolderA } @@ -695,11 +695,11 @@ suite('ExtHostSearch', () => { test('no results', async () => { await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { - return TPromise.wrap(null); + return Promise.resolve(null); } }); - const { results, stats } = await runTextSearch(getPattern('foo'), getSimpleQuery()); + const { results, stats } = await runTextSearch(getSimpleQuery('foo')); assert(!stats.limitHit); assert(!results.length); }); @@ -713,11 +713,11 @@ suite('ExtHostSearch', () => { await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); - const { results, stats } = await runTextSearch(getPattern('foo'), getSimpleQuery()); + const { results, stats } = await runTextSearch(getSimpleQuery('foo')); assert(!stats.limitHit); assertResults(results, providedResults); }); @@ -727,12 +727,13 @@ suite('ExtHostSearch', () => { provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { assert.equal(options.includes.length, 1); assert.equal(options.excludes.length, 1); - return TPromise.wrap(null); + return Promise.resolve(null); } }); - const query: IRawSearchQuery = { + const query: ITextQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), includePattern: { '*.ts': true @@ -748,7 +749,7 @@ suite('ExtHostSearch', () => { ] }; - await runTextSearch(getPattern('foo'), query); + await runTextSearch(query); }); test('global/local include/excludes combined', async () => { @@ -762,12 +763,13 @@ suite('ExtHostSearch', () => { assert.deepEqual(options.excludes.sort(), ['*.js']); } - return TPromise.wrap(null); + return Promise.resolve(null); } }); - const query: IRawSearchQuery = { + const query: ITextQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), includePattern: { '*.ts': true @@ -789,7 +791,7 @@ suite('ExtHostSearch', () => { ] }; - await runTextSearch(getPattern('foo'), query); + await runTextSearch(query); }); test('include/excludes resolved correctly', async () => { @@ -798,12 +800,13 @@ suite('ExtHostSearch', () => { assert.deepEqual(options.includes.sort(), ['*.jsx', '*.ts']); assert.deepEqual(options.excludes.sort(), []); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), includePattern: { '*.ts': true, @@ -826,7 +829,7 @@ suite('ExtHostSearch', () => { ] }; - await runTextSearch(getPattern('foo'), query); + await runTextSearch(query); }); test('provider fail', async () => { @@ -837,7 +840,7 @@ suite('ExtHostSearch', () => { }); try { - await runTextSearch(getPattern('foo'), getSimpleQuery()); + await runTextSearch(getSimpleQuery('foo')); assert(false, 'Expected to fail'); } catch { // expected to fail @@ -864,12 +867,13 @@ suite('ExtHostSearch', () => { await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), excludePattern: { '*.js': { @@ -882,7 +886,7 @@ suite('ExtHostSearch', () => { ] }; - const { results } = await runTextSearch(getPattern('foo'), query); + const { results } = await runTextSearch(query); assertResults(results, providedResults.slice(1)); }); @@ -923,12 +927,13 @@ suite('ExtHostSearch', () => { } reportedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), excludePattern: { '*.js': { @@ -954,7 +959,7 @@ suite('ExtHostSearch', () => { ] }; - const { results } = await runTextSearch(getPattern('foo'), query); + const { results } = await runTextSearch(query); assertResults(results, [ makeTextResult(rootFolderA, 'folder/fileA.scss'), makeTextResult(rootFolderA, 'folder/file2.css'), @@ -972,12 +977,13 @@ suite('ExtHostSearch', () => { await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), includePattern: { '*.ts': true @@ -988,7 +994,7 @@ suite('ExtHostSearch', () => { ] }; - const { results } = await runTextSearch(getPattern('foo'), query); + const { results } = await runTextSearch(query); assertResults(results, providedResults.slice(1)); }); @@ -1003,12 +1009,13 @@ suite('ExtHostSearch', () => { provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), maxResults: 1, @@ -1017,7 +1024,7 @@ suite('ExtHostSearch', () => { ] }; - const { results, stats } = await runTextSearch(getPattern('foo'), query); + const { results, stats } = await runTextSearch(query); assert(stats.limitHit, 'Expected to return limitHit'); assertResults(results, providedResults.slice(0, 1)); assert(wasCanceled, 'Expected to be canceled'); @@ -1035,12 +1042,13 @@ suite('ExtHostSearch', () => { provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), maxResults: 2, @@ -1049,7 +1057,7 @@ suite('ExtHostSearch', () => { ] }; - const { results, stats } = await runTextSearch(getPattern('foo'), query); + const { results, stats } = await runTextSearch(query); assert(stats.limitHit, 'Expected to return limitHit'); assertResults(results, providedResults.slice(0, 2)); assert(wasCanceled, 'Expected to be canceled'); @@ -1066,12 +1074,13 @@ suite('ExtHostSearch', () => { provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), maxResults: 2, @@ -1080,7 +1089,7 @@ suite('ExtHostSearch', () => { ] }; - const { results, stats } = await runTextSearch(getPattern('foo'), query); + const { results, stats } = await runTextSearch(query); assert(!stats.limitHit, 'Expected not to return limitHit'); assertResults(results, providedResults); assert(!wasCanceled, 'Expected not to be canceled'); @@ -1096,12 +1105,13 @@ suite('ExtHostSearch', () => { await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); - return TPromise.wrap({ limitHit: true }); + return Promise.resolve({ limitHit: true }); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), maxResults: 1000, @@ -1110,7 +1120,7 @@ suite('ExtHostSearch', () => { ] }; - const { results, stats } = await runTextSearch(getPattern('foo'), query); + const { results, stats } = await runTextSearch(query); assert(stats.limitHit, 'Expected to return limitHit'); assertResults(results, providedResults); }); @@ -1120,7 +1130,7 @@ suite('ExtHostSearch', () => { await registerTestTextSearchProvider({ async provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Promise { token.onCancellationRequested(() => cancels++); - await new TPromise(r => process.nextTick(r)); + await new Promise(r => process.nextTick(r)); [ 'file1.ts', 'file2.ts', @@ -1132,6 +1142,7 @@ suite('ExtHostSearch', () => { const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), maxResults: 2, @@ -1141,7 +1152,7 @@ suite('ExtHostSearch', () => { ] }; - const { results } = await runTextSearch(getPattern('foo'), query); + const { results } = await runTextSearch(query); assert.equal(results.length, 2); assert.equal(cancels, 2); }); @@ -1156,19 +1167,20 @@ suite('ExtHostSearch', () => { await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }, fancyScheme); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), folderQueries: [ { folder: fancySchemeFolderA } ] }; - const { results } = await runTextSearch(getPattern('foo'), query); + const { results } = await runTextSearch(query); assertResults(results, providedResults); }); }); 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 4f28c65d369..6e43ee0967c 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts @@ -16,7 +16,6 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { MainThreadCommands } from 'vs/workbench/api/electron-browser/mainThreadCommands'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; -import { TPromise } from 'vs/base/common/winjs.base'; import { TreeItemCollapsibleState, ITreeItem } from 'vs/workbench/common/views'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -29,11 +28,11 @@ suite('ExtHostTreeView', function () { $registerTreeViewDataProvider(treeViewId: string): void { } - $refresh(viewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): TPromise { - return TPromise.as(null).then(() => this.onRefresh.fire(itemsToRefresh)); + $refresh(viewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Promise { + return Promise.resolve(null).then(() => this.onRefresh.fire(itemsToRefresh)); } - $reveal(): TPromise { + $reveal(): Promise { return null; } @@ -84,12 +83,12 @@ suite('ExtHostTreeView', function () { .then(elements => { const actuals = elements.map(e => e.handle); assert.deepEqual(actuals, ['0/0:a', '0/0:b']); - return TPromise.join([ + return Promise.all([ testObject.$getChildren('testNodeTreeProvider', '0/0:a') .then(children => { const actuals = children.map(e => e.handle); assert.deepEqual(actuals, ['0/0:a/0:aa', '0/0:a/0:ab']); - return TPromise.join([ + return Promise.all([ testObject.$getChildren('testNodeTreeProvider', '0/0:a/0:aa').then(children => assert.equal(children.length, 0)), testObject.$getChildren('testNodeTreeProvider', '0/0:a/0:ab').then(children => assert.equal(children.length, 0)) ]); @@ -98,7 +97,7 @@ suite('ExtHostTreeView', function () { .then(children => { const actuals = children.map(e => e.handle); assert.deepEqual(actuals, ['0/0:b/0:ba', '0/0:b/0:bb']); - return TPromise.join([ + return Promise.all([ testObject.$getChildren('testNodeTreeProvider', '0/0:b/0:ba').then(children => assert.equal(children.length, 0)), testObject.$getChildren('testNodeTreeProvider', '0/0:b/0:bb').then(children => assert.equal(children.length, 0)) ]); @@ -112,12 +111,12 @@ suite('ExtHostTreeView', function () { .then(elements => { const actuals = elements.map(e => e.handle); assert.deepEqual(actuals, ['1/a', '1/b']); - return TPromise.join([ + return Promise.all([ testObject.$getChildren('testNodeWithIdTreeProvider', '1/a') .then(children => { const actuals = children.map(e => e.handle); assert.deepEqual(actuals, ['1/aa', '1/ab']); - return TPromise.join([ + return Promise.all([ testObject.$getChildren('testNodeWithIdTreeProvider', '1/aa').then(children => assert.equal(children.length, 0)), testObject.$getChildren('testNodeWithIdTreeProvider', '1/ab').then(children => assert.equal(children.length, 0)) ]); @@ -126,7 +125,7 @@ suite('ExtHostTreeView', function () { .then(children => { const actuals = children.map(e => e.handle); assert.deepEqual(actuals, ['1/ba', '1/bb']); - return TPromise.join([ + return Promise.all([ testObject.$getChildren('testNodeWithIdTreeProvider', '1/ba').then(children => assert.equal(children.length, 0)), testObject.$getChildren('testNodeWithIdTreeProvider', '1/bb').then(children => assert.equal(children.length, 0)) ]); @@ -165,7 +164,7 @@ suite('ExtHostTreeView', function () { }); test('refresh a parent node', () => { - return new TPromise((c, e) => { + return new Promise((c, e) => { target.onRefresh.event(actuals => { assert.deepEqual(['0/0:b'], Object.keys(actuals)); assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), { 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 b763fdf321e..ce851d814f6 100644 --- a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts @@ -9,7 +9,7 @@ import * as fs from 'fs'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; -import { ISearchService, IQueryOptions } from 'vs/platform/search/common/search'; +import { ISearchService } from 'vs/platform/search/common/search'; import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -28,7 +28,7 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; -import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; +import { QueryBuilder, ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import * as event from 'vs/base/common/event'; import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; @@ -73,7 +73,7 @@ suite.skip('TextSearch performance (integration)', () => { [ILogService, new NullLogService()] )); - const queryOptions: IQueryOptions = { + const queryOptions: ITextQueryBuilderOptions = { maxResults: 2048 }; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 0fb71a350f2..7addc3b6911 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -883,7 +883,7 @@ export class TestFileService implements IFileService { } activateProvider(_scheme: string) { - return TPromise.as(null); + return Promise.resolve(null); } canHandleResource(resource: URI): boolean { diff --git a/test/smoke/src/areas/problems/problems.ts b/test/smoke/src/areas/problems/problems.ts index bffd6a95fca..29c330dc7d5 100644 --- a/test/smoke/src/areas/problems/problems.ts +++ b/test/smoke/src/areas/problems/problems.ts @@ -40,7 +40,7 @@ export class Problems { public static getSelectorInProblemsView(problemType: ProblemSeverity): string { let selector = problemType === ProblemSeverity.WARNING ? 'warning' : 'error'; - return `div[aria-label="Problems grouped by files"] .icon.${selector}`; + return `div[id="workbench.panel.markers"] .monaco-tl-contents .icon.${selector}`; } public static getSelectorInEditor(problemType: ProblemSeverity): string { diff --git a/yarn.lock b/yarn.lock index e39e168182b..e9f893ecbc5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9325,10 +9325,10 @@ vscode-nsfw@1.1.1: lodash.isundefined "^3.0.1" nan "^2.10.0" -vscode-ripgrep@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.2.2.tgz#c21b91bb558638a7124fe3d9719b2666b12dbfdc" - integrity sha512-rPbCj4LUnPzivezk3pC94G5sB17HbayjV9mSvNTDYFg5llHN/XozAmkoDOC0KG9UveuQ71MADX1//CeHtUTmZw== +vscode-ripgrep@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.2.4.tgz#b3cfbe08ed13f6cf6b134147ea4d982970ab4f70" + integrity sha512-TysaK20aCSfsFIQGd0DfMshjkHof0fG6zx7DoO0tdWNAZgsvoqLtOWdqHcocICRZ3RSpdiMiEJRaMK+iOzx16w== vscode-sqlite3@4.0.2: version "4.0.2" @@ -9344,10 +9344,10 @@ vscode-textmate@^4.0.1: dependencies: oniguruma "^7.0.0" -vscode-xterm@3.9.0-beta9: - version "3.9.0-beta9" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.9.0-beta9.tgz#4b03dc89603bab4ed5e65b5dd3c26d73275f6113" - integrity sha512-l4Gvqm7unG4JoGgkOPydmDadl81zpqK7PRfmilSlO7EIYhPYvVjxoU2OhdsPfPRkyinLLbSfI9MGuR+6no7UEw== +vscode-xterm@3.9.0-beta11: + version "3.9.0-beta11" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.9.0-beta11.tgz#f90bb8bb03f9db2a7369b37832704f23a7720c73" + integrity sha512-2KjtCLADUNj9a0qDYExUOoJwgdL9TgP/hGFolzSO1j1LqZIz785qFhoM5ovnJ7jLjU+BsmfQpeNKYaUnp5fbEg== vso-node-api@6.1.2-preview: version "6.1.2-preview"