From 382ea05fe572c39d974224d4e87429f34a02af79 Mon Sep 17 00:00:00 2001 From: Nilesh Date: Mon, 14 Oct 2019 19:52:33 +0530 Subject: [PATCH 01/58] CommandAndKeybinding for adding multi cursor from search result --- .../search/browser/search.contribution.ts | 15 ++++++++++ .../contrib/search/browser/searchView.ts | 28 ++++++++++++++++++- .../contrib/search/common/constants.ts | 1 + 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 87140d0b464..e24a2cb3cf0 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -617,6 +617,21 @@ KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({ handler: toggleRegexCommand }, ToggleRegexKeybinding)); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.AddCursorsAtSearchResults, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.FileMatchOrMatchFocusKey), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L, + handler: (accessor, args: any) => { + const searchView = getSearchView(accessor.get(IViewletService), accessor.get(IPanelService)); + if (searchView) { + const tree: WorkbenchObjectTree = searchView.getControl(); + searchView.openEditorWithMultiCursor(tree.getFocus()[0]); + } + } +}); + registry.registerWorkbenchAction(new SyncActionDescriptor(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL), 'Search: Collapse All', category); registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllSymbolsAction, ShowAllSymbolsAction.ID, ShowAllSymbolsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_T }), 'Go to Symbol in Workspace...'); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 326e78344bb..0d179b37199 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -20,7 +20,7 @@ import * as env from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/searchview'; -import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, isCodeEditor, isDiffEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import * as nls from 'vs/nls'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -61,6 +61,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { MultiCursorSelectionController } from 'vs/editor/contrib/multicursor/multicursor'; +import { CommonFindController } from 'vs/editor/contrib/find/findController'; const $ = dom.$; @@ -1538,6 +1540,30 @@ export class SearchView extends ViewletPanel { }, errors.onUnexpectedError); } + openEditorWithMultiCursor(element: FileMatchOrMatch): Promise { + const selection = this.getSelectionFrom(element); + const resource = element instanceof Match ? element.parent().resource : (element).resource; + return this.editorService.openEditor({ + resource: resource, + options: { + preserveFocus: false, + pinned: true, + selection, + revealIfVisible: true + } + }).then(editor => { + if (editor) { + let codeEditor = getCodeEditor(editor.getControl()); + if (codeEditor) { + let multiCursorController = MultiCursorSelectionController.get(codeEditor); + let findController = CommonFindController.get(codeEditor); + multiCursorController.selectAll(findController); + } + } + this.viewModel.searchResult.rangeHighlightDecorations.removeHighlightRange(); + }, errors.onUnexpectedError); + } + private getSelectionFrom(element: FileMatchOrMatch): any { let match: Match | null = null; if (element instanceof Match) { diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index d2261e85097..1a99b4b6c31 100644 --- a/src/vs/workbench/contrib/search/common/constants.ts +++ b/src/vs/workbench/contrib/search/common/constants.ts @@ -24,6 +24,7 @@ export const CloseReplaceWidgetActionId = 'closeReplaceInFilesWidget'; export const ToggleCaseSensitiveCommandId = 'toggleSearchCaseSensitive'; export const ToggleWholeWordCommandId = 'toggleSearchWholeWord'; export const ToggleRegexCommandId = 'toggleSearchRegex'; +export const AddCursorsAtSearchResults = 'addCursorsAtSearchResults'; export const RevealInSideBarForSearchResults = 'search.action.revealInSideBar'; export const ToggleSearchViewPositionCommandId = 'search.action.toggleSearchViewPosition'; From 1982664a2b37a86275a2ab921d784fa4d2840521 Mon Sep 17 00:00:00 2001 From: Nilesh Date: Tue, 29 Oct 2019 09:38:44 +0530 Subject: [PATCH 02/58] Utilized searchString and other parameters for putting multi cursor Created function selectAllUsingString for this purpose. --- src/vs/editor/contrib/multicursor/multicursor.ts | 14 ++++++++++++++ .../workbench/contrib/search/browser/searchView.ts | 11 ++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index a76a1d62e96..b942239be10 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -623,6 +623,20 @@ export class MultiCursorSelectionController extends Disposable implements IEdito this._setSelections(matches.map(m => new Selection(m.range.startLineNumber, m.range.startColumn, m.range.endLineNumber, m.range.endColumn))); } } + + public selectAllUsingString(searchString: string, isRegex: boolean, matchCase: boolean, wholeWord: boolean, limitResultCount?: number): void { + if (!this._editor.hasModel()) { + return; + } + + let matches: FindMatch[] | null = null; + + matches = this._editor.getModel().findMatches(searchString, true, isRegex, matchCase, wholeWord ? this._editor.getOption(EditorOption.wordSeparators) : null, false, Constants.MAX_SAFE_SMALL_INTEGER); + + if (matches.length > 0) { + this._setSelections(matches.map(m => new Selection(m.range.startLineNumber, m.range.startColumn, m.range.endLineNumber, m.range.endColumn))); + } + } } export abstract class MultiCursorSelectionControllerAction extends EditorAction { diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 0d179b37199..74a182ab742 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -62,7 +62,6 @@ import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { MultiCursorSelectionController } from 'vs/editor/contrib/multicursor/multicursor'; -import { CommonFindController } from 'vs/editor/contrib/find/findController'; const $ = dom.$; @@ -1541,14 +1540,12 @@ export class SearchView extends ViewletPanel { } openEditorWithMultiCursor(element: FileMatchOrMatch): Promise { - const selection = this.getSelectionFrom(element); const resource = element instanceof Match ? element.parent().resource : (element).resource; return this.editorService.openEditor({ resource: resource, options: { preserveFocus: false, pinned: true, - selection, revealIfVisible: true } }).then(editor => { @@ -1556,8 +1553,12 @@ export class SearchView extends ViewletPanel { let codeEditor = getCodeEditor(editor.getControl()); if (codeEditor) { let multiCursorController = MultiCursorSelectionController.get(codeEditor); - let findController = CommonFindController.get(codeEditor); - multiCursorController.selectAll(findController); + let isRegex = this.searchWidget.searchInput.getRegex(); + let isWholeWords = this.searchWidget.searchInput.getWholeWords(); + let isCaseSensitive = this.searchWidget.searchInput.getCaseSensitive(); + let contentPattern = this.searchWidget.searchInput.getValue(); + + multiCursorController.selectAllUsingString(contentPattern, isRegex, isCaseSensitive, isWholeWords); } } this.viewModel.searchResult.rangeHighlightDecorations.removeHighlightRange(); From 33c241ba1fb0d9a2d825652094f96a5247ec448e Mon Sep 17 00:00:00 2001 From: Matthew Clifford <35276477+smilegodly@users.noreply.github.com> Date: Wed, 30 Oct 2019 16:46:06 -0400 Subject: [PATCH 03/58] Removed duplicate clear function definitions Modified focus search bar to be part of clearing search bar function. --- .../contrib/preferences/browser/settingsEditor2.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 9e77e62fd77..a2ba75eda66 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -370,6 +370,7 @@ export class SettingsEditor2 extends BaseEditor { clearSearchResults(): void { this.searchWidget.setValue(''); + this.focusSearch(); } clearSearchFilters(): void { @@ -382,17 +383,12 @@ export class SettingsEditor2 extends BaseEditor { this.searchWidget.setValue(query.trim()); } - clearSearch(): void { - this.clearSearchResults(); - this.focusSearch(); - } - private createHeader(parent: HTMLElement): void { this.headerContainer = DOM.append(parent, $('.settings-header')); const searchContainer = DOM.append(this.headerContainer, $('.search-container')); - const clearInputAction = new Action(SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Settings Search Input"), 'codicon-clear-all', false, () => { this.clearSearch(); return Promise.resolve(null); }); + const clearInputAction = new Action(SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Settings Search Input"), 'codicon-clear-all', false, () => { this.clearSearchResults(); return Promise.resolve(null); }); const searchBoxLabel = localize('SearchSettings.AriaLabel', "Search settings"); this.searchWidget = this._register(this.instantiationService.createInstance(SuggestEnabledInput, `${SettingsEditor2.ID}.searchbox`, searchContainer, { @@ -536,7 +532,6 @@ export class SettingsEditor2 extends BaseEditor { this._register(DOM.addDisposableListener(clearSearch, DOM.EventType.CLICK, (e: MouseEvent) => { DOM.EventHelper.stop(e, false); this.clearSearchResults(); - this.focusSearch(); })); DOM.append(this.noResultsMessage, clearSearchContainer); From 76fffc0a22aa33ba63c0e8eb9a9807722bcbc82d Mon Sep 17 00:00:00 2001 From: grey275 Date: Sun, 10 Nov 2019 13:31:35 -0800 Subject: [PATCH 04/58] implement/register RenameActiveTerminalCommand --- .../terminal/browser/terminal.contribution.ts | 25 +++++++++++++++++- .../terminal/browser/terminalActions.ts | 26 +++++++++++++++++++ .../contrib/terminal/common/terminal.ts | 1 + 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 485539d452b..87aceaad1f1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -20,7 +20,7 @@ import * as panel from 'vs/workbench/browser/panel'; import { getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { Extensions as QuickOpenExtensions, IQuickOpenRegistry, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; -import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand, CreateNewWithCwdTerminalCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand, CreateNewWithCwdTerminalCommand, RenameActiveTerminalCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -583,6 +583,7 @@ if (BrowserFeatures.clipboard.readText) { }] } })).register(); + (new CreateNewWithCwdTerminalCommand({ id: CreateNewWithCwdTerminalCommand.ID, precondition: undefined, @@ -604,6 +605,28 @@ if (BrowserFeatures.clipboard.readText) { } })).register(); +(new RenameActiveTerminalCommand({ + id: RenameActiveTerminalCommand.ID, + precondition: undefined, + description: { + description: RenameActiveTerminalCommand.LABEL, + args: [{ + name: 'args', + schema: { + type: 'object', + required: ['newName'], + properties: { + newName: { + description: RenameActiveTerminalCommand.NEW_NAME_ARG_LABEL, + type: 'string', + minLength: 1 + } + } + } + }] + } +})).register(); + setupTerminalCommands(); setupTerminalMenu(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 2971096e09d..b794858c40e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -1065,6 +1065,32 @@ export class RenameTerminalAction extends Action { }); } } +export class RenameActiveTerminalCommand extends Command { + public static readonly ID = TERMINAL_COMMAND_ID.RENAME_NONINTERACTIVE; + public static readonly LABEL = nls.localize( + 'workbench.action.terminal.RenameNonInteractive', + 'Rename the Currently Active Terminal' + ); + public static readonly NEW_NAME_ARG_LABEL = nls.localize( + 'workbench.action.terminal.RenameNonInteractive.newName', + 'The new name for the terminal' + ); + + public runCommand( + accessor: ServicesAccessor, + args: { + newName: string; + } + ): void { + const terminalInstance = accessor.get(ITerminalService).getActiveInstance(); + + if (!terminalInstance) { + return; + } + + terminalInstance.setTitle(args.newName, TitleEventSource.Api); + } +} export class FocusTerminalFindWidgetAction extends Action { diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 8527a18f6c4..31526edd8bd 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -452,6 +452,7 @@ export const enum TERMINAL_COMMAND_ID { CLEAR_SELECTION = 'workbench.action.terminal.clearSelection', MANAGE_WORKSPACE_SHELL_PERMISSIONS = 'workbench.action.terminal.manageWorkspaceShellPermissions', RENAME = 'workbench.action.terminal.rename', + RENAME_NONINTERACTIVE = 'workbench.action.terminal.RenameNonInteractive', FIND_WIDGET_FOCUS = 'workbench.action.terminal.focusFindWidget', FIND_WIDGET_HIDE = 'workbench.action.terminal.hideFindWidget', QUICK_OPEN_TERM = 'workbench.action.quickOpenTerm', From 62feea2b1e3419f28d2709dfd8024b68fb888ef3 Mon Sep 17 00:00:00 2001 From: grey275 Date: Sun, 10 Nov 2019 13:46:35 -0800 Subject: [PATCH 05/58] ensure command is in proper camelcase --- src/vs/workbench/contrib/terminal/browser/terminalActions.ts | 4 ++-- src/vs/workbench/contrib/terminal/common/terminal.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index b794858c40e..8c033277f51 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -1068,11 +1068,11 @@ export class RenameTerminalAction extends Action { export class RenameActiveTerminalCommand extends Command { public static readonly ID = TERMINAL_COMMAND_ID.RENAME_NONINTERACTIVE; public static readonly LABEL = nls.localize( - 'workbench.action.terminal.RenameNonInteractive', + 'workbench.action.terminal.renameNoninteractive', 'Rename the Currently Active Terminal' ); public static readonly NEW_NAME_ARG_LABEL = nls.localize( - 'workbench.action.terminal.RenameNonInteractive.newName', + 'workbench.action.terminal.renameNoninteractive.newName', 'The new name for the terminal' ); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 31526edd8bd..7267dff1fb2 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -452,7 +452,7 @@ export const enum TERMINAL_COMMAND_ID { CLEAR_SELECTION = 'workbench.action.terminal.clearSelection', MANAGE_WORKSPACE_SHELL_PERMISSIONS = 'workbench.action.terminal.manageWorkspaceShellPermissions', RENAME = 'workbench.action.terminal.rename', - RENAME_NONINTERACTIVE = 'workbench.action.terminal.RenameNonInteractive', + RENAME_NONINTERACTIVE = 'workbench.action.terminal.renameNoninteractive', FIND_WIDGET_FOCUS = 'workbench.action.terminal.focusFindWidget', FIND_WIDGET_HIDE = 'workbench.action.terminal.hideFindWidget', QUICK_OPEN_TERM = 'workbench.action.quickOpenTerm', From 231d559f7041431dc234c51975c1774a734269f6 Mon Sep 17 00:00:00 2001 From: grey275 Date: Mon, 11 Nov 2019 21:08:02 -0800 Subject: [PATCH 06/58] warn user if we try to rename the active terminal when none exists --- src/vs/workbench/contrib/terminal/browser/terminalActions.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 8c033277f51..e8af4e37015 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -1082,9 +1082,14 @@ export class RenameActiveTerminalCommand extends Command { newName: string; } ): void { + const notificationService = accessor.get(INotificationService); const terminalInstance = accessor.get(ITerminalService).getActiveInstance(); if (!terminalInstance) { + notificationService.warn(nls.localize( + RenameActiveTerminalCommand.LABEL + '.noActiveTerminal', + 'No active terminal to rename' + )); return; } From 695bc30383884a6d5fef1e17fd4a42a8799f2f4f Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Sun, 24 Nov 2019 02:35:39 -0800 Subject: [PATCH 07/58] Add initial search editor implementation (#85424) * Initial search editor implementation * Merge fixes * Exclude search result untitled editors from localresults results * Remove unneeded keybinding * .vsc-search => .code-search * Add extension readme * Fix extra space inserted on Windows * Fixup some escaping issues * Add highlighting of result matches --- extensions/search-result/README.md | 3 + extensions/search-result/package.json | 41 ++ extensions/search-result/package.nls.json | 4 + extensions/search-result/src/extension.ts | 143 +++++ .../search-result/src/typings/refs.d.ts | 7 + .../syntaxes/searchResult.tmLanguage.json | 18 + extensions/search-result/tsconfig.json | 9 + extensions/search-result/yarn.lock | 602 ++++++++++++++++++ .../search/browser/search.contribution.ts | 9 +- .../contrib/search/browser/searchActions.ts | 36 +- .../contrib/search/browser/searchEditor.ts | 158 +++++ .../contrib/search/browser/searchView.ts | 56 +- .../contrib/search/common/constants.ts | 2 + .../contrib/search/common/searchModel.ts | 6 + .../services/search/common/search.ts | 2 + .../services/search/common/searchService.ts | 6 + 16 files changed, 1091 insertions(+), 11 deletions(-) create mode 100644 extensions/search-result/README.md create mode 100644 extensions/search-result/package.json create mode 100644 extensions/search-result/package.nls.json create mode 100644 extensions/search-result/src/extension.ts create mode 100644 extensions/search-result/src/typings/refs.d.ts create mode 100644 extensions/search-result/syntaxes/searchResult.tmLanguage.json create mode 100644 extensions/search-result/tsconfig.json create mode 100644 extensions/search-result/yarn.lock create mode 100644 src/vs/workbench/contrib/search/browser/searchEditor.ts diff --git a/extensions/search-result/README.md b/extensions/search-result/README.md new file mode 100644 index 00000000000..a28a54db1b8 --- /dev/null +++ b/extensions/search-result/README.md @@ -0,0 +1,3 @@ +# Language Features for Search Result files + +**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json new file mode 100644 index 00000000000..3639f83366d --- /dev/null +++ b/extensions/search-result/package.json @@ -0,0 +1,41 @@ +{ + "name": "search-result", + "displayName": "%displayName%", + "description": "%description%", + "version": "1.0.0", + "publisher": "vscode", + "license": "MIT", + "engines": { + "vscode": "^1.39.0" + }, + "categories": [ + "Programming Languages" + ], + "main": "out/extension.js", + "activationEvents": [ + "*" + ], + "contributes": { + "languages": [ + { + "id": "search-result", + "extensions": [ + ".code-search" + ], + "aliases": [ + "Search Result" + ] + } + ], + "grammars": [ + { + "language": "search-result", + "scopeName": "text.searchResult", + "path": "./syntaxes/searchResult.tmLanguage.json" + } + ] + }, + "devDependencies": { + "vscode": "^1.1.36" + } +} diff --git a/extensions/search-result/package.nls.json b/extensions/search-result/package.nls.json new file mode 100644 index 00000000000..324fd97bcd2 --- /dev/null +++ b/extensions/search-result/package.nls.json @@ -0,0 +1,4 @@ +{ + "displayName": "Search Result", + "description": "Provides syntax highlighting and language features for tabbed search results." +} diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts new file mode 100644 index 00000000000..e538bd7fab3 --- /dev/null +++ b/extensions/search-result/src/extension.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as pathUtils from 'path'; + +const FILE_LINE_REGEX = /^(\S.*):$/; +const RESULT_LINE_REGEX = /^(\s+)(\d+):(\s+)(.*)$/; + +let cachedLastParse: { version: number, parse: ParsedSearchResults } | undefined; + +export function activate() { + + vscode.languages.registerDefinitionProvider({ language: 'search-result' }, { + provideDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.DefinitionLink[] { + const lineResult = parseSearchResults(document, token)[position.line]; + if (!lineResult) { return []; } + if (lineResult.type === 'file') { + return lineResult.allLocations.length > 1 ? lineResult.allLocations : [lineResult.location]; + } + + return [lineResult.location]; + } + }); + + vscode.languages.registerDocumentLinkProvider({ language: 'search-result' }, { + async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { + return parseSearchResults(document, token) + .filter(({ type }) => type === 'file') + .map(({ location }) => ({ range: location.originSelectionRange!, target: location.targetUri })); + } + }); + + vscode.window.onDidChangeActiveTextEditor(e => { + if (e?.document.languageId === 'search-result') { + // Clear the parse whenever we open a new editor. + // Conservative because things like the URI might remain constant even if the contents change, and re-parsing even large files is relatively fast. + cachedLastParse = undefined; + } + }); +} + + +function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | undefined { + if (pathUtils.isAbsolute(path)) { return vscode.Uri.file(path); } + if (path.indexOf('~/') === 0) { + return vscode.Uri.file(pathUtils.join(process.env.HOME!, path.slice(2))); + } + + + if (vscode.workspace.workspaceFolders) { + const multiRootFormattedPath = /^(.*) • (.*)$/.exec(path); + if (multiRootFormattedPath) { + const [, workspaceName, workspacePath] = multiRootFormattedPath; + const folder = vscode.workspace.workspaceFolders.filter(wf => wf.name === workspaceName)[0]; + if (folder) { + return vscode.Uri.file(pathUtils.join(folder.uri.fsPath, workspacePath)); + } + } + + else if (vscode.workspace.workspaceFolders.length === 1) { + return vscode.Uri.file(pathUtils.join(vscode.workspace.workspaceFolders[0].uri.fsPath, path)); + } else if (resultsUri.scheme !== 'untitled') { + // We're in a multi-root workspace, but the path is not multi-root formatted + // Possibly a saved search from a single root session. Try checking if the search result document's URI is in a current workspace folder. + const prefixMatch = vscode.workspace.workspaceFolders.filter(wf => resultsUri.toString().startsWith(wf.uri.toString()))[0]; + if (prefixMatch) { return vscode.Uri.file(pathUtils.join(prefixMatch.uri.fsPath, path)); } + } + } + + console.error(`Unable to resolve path ${path}`); + return undefined; +} + +type ParsedSearchResults = Array< + { type: 'file', location: vscode.LocationLink, allLocations: vscode.LocationLink[] } | + { type: 'result', location: vscode.LocationLink } +>; + +function parseSearchResults(document: vscode.TextDocument, token: vscode.CancellationToken): ParsedSearchResults { + + if (cachedLastParse && cachedLastParse.version === document.version) { + return cachedLastParse.parse; + } + + const lines = document.getText().split(/\r?\n/); + const links: ParsedSearchResults = []; + + let currentTarget: vscode.Uri | undefined = undefined; + let currentTargetLocations: vscode.LocationLink[] | undefined = undefined; + + for (let i = 0; i < lines.length; i++) { + if (token.isCancellationRequested) { return []; } + const line = lines[i]; + + const fileLine = FILE_LINE_REGEX.exec(line); + if (fileLine) { + const [, path] = fileLine; + + currentTarget = relativePathToUri(path, document.uri); + if (!currentTarget) { continue; } + currentTargetLocations = []; + + const location: vscode.LocationLink = { + targetRange: new vscode.Range(0, 0, 0, 1), + targetUri: currentTarget, + originSelectionRange: new vscode.Range(i, 0, i, line.length), + }; + + + links[i] = { type: 'file', location, allLocations: currentTargetLocations }; + } + + if (!currentTarget) { continue; } + + const resultLine = RESULT_LINE_REGEX.exec(line); + if (resultLine) { + const [, indentation, _lineNumber, resultIndentation] = resultLine; + const lineNumber = +_lineNumber - 1; + const resultStart = (indentation + _lineNumber + ':' + resultIndentation).length; + + const location: vscode.LocationLink = { + targetRange: new vscode.Range(Math.max(lineNumber - 3, 0), 0, lineNumber + 3, line.length), + targetSelectionRange: new vscode.Range(lineNumber, 0, lineNumber, line.length), + targetUri: currentTarget, + originSelectionRange: new vscode.Range(i, resultStart, i, line.length), + }; + + currentTargetLocations?.push(location); + + links[i] = { type: 'result', location }; + } + } + + cachedLastParse = { + version: document.version, + parse: links + }; + + return links; +} diff --git a/extensions/search-result/src/typings/refs.d.ts b/extensions/search-result/src/typings/refs.d.ts new file mode 100644 index 00000000000..c9849d48e08 --- /dev/null +++ b/extensions/search-result/src/typings/refs.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// diff --git a/extensions/search-result/syntaxes/searchResult.tmLanguage.json b/extensions/search-result/syntaxes/searchResult.tmLanguage.json new file mode 100644 index 00000000000..80e9edfaa81 --- /dev/null +++ b/extensions/search-result/syntaxes/searchResult.tmLanguage.json @@ -0,0 +1,18 @@ +{ + "name": "Search Results", + "scopeName": "text.searchResult", + "patterns": [ + { + "match": "^# (Query|Flags|Include|Exclude): .*$", + "name": "comment" + }, + { + "match": "^\\S.*:$", + "name": "string path.searchResult" + }, + { + "match": "^ \\d+", + "name": "constant.numeric lineNumber.searchResult" + } + ] +} diff --git a/extensions/search-result/tsconfig.json b/extensions/search-result/tsconfig.json new file mode 100644 index 00000000000..16ed233f6e8 --- /dev/null +++ b/extensions/search-result/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../shared.tsconfig.json", + "compilerOptions": { + "outDir": "./out", + }, + "include": [ + "src/**/*" + ] +} diff --git a/extensions/search-result/yarn.lock b/extensions/search-result/yarn.lock new file mode 100644 index 00000000000..4ff83ab98bb --- /dev/null +++ b/extensions/search-result/yarn.lock @@ -0,0 +1,602 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +agent-base@4, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +ajv@^6.5.5: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + +escape-string-regexp@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.2: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= + +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== + dependencies: + agent-base "4" + debug "3.1.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@^2.2.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +mime-db@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== + dependencies: + mime-db "1.40.0" + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +mocha@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" + integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== + dependencies: + browser-stdout "1.3.1" + commander "2.15.1" + debug "3.1.0" + diff "3.5.0" + escape-string-regexp "1.0.5" + glob "7.1.2" + growl "1.10.5" + he "1.1.1" + minimatch "3.0.4" + mkdirp "0.5.1" + supports-color "5.4.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +psl@^1.1.24: + version "1.4.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2" + integrity sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw== + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + +request@^2.88.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +safe-buffer@^5.0.1, safe-buffer@^5.1.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^5.4.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +source-map-support@^0.5.0: + version "0.5.16" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +supports-color@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== + dependencies: + has-flag "^3.0.0" + +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== + dependencies: + psl "^1.1.24" + punycode "^1.4.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +url-parse@^1.4.4: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +uuid@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" + integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vscode-test@^0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8" + integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w== + dependencies: + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + +vscode@^1.1.36: + version "1.1.36" + resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6" + integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ== + dependencies: + glob "^7.1.2" + mocha "^5.2.0" + request "^2.88.0" + semver "^5.4.1" + source-map-support "^0.5.0" + url-parse "^1.4.4" + vscode-test "^0.4.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 7cf58147ab2..94180385cbb 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -41,7 +41,7 @@ import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition import { OpenAnythingHandler } from 'vs/workbench/contrib/search/browser/openAnythingHandler'; import { OpenSymbolHandler } from 'vs/workbench/contrib/search/browser/openSymbolHandler'; import { registerContributions as replaceContributions } from 'vs/workbench/contrib/search/browser/replaceContributions'; -import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction } from 'vs/workbench/contrib/search/browser/searchActions'; +import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction, OpenResultsInEditorAction } from 'vs/workbench/contrib/search/browser/searchActions'; import { SearchPanel } from 'vs/workbench/contrib/search/browser/searchPanel'; import { SearchView, SearchViewPosition } from 'vs/workbench/contrib/search/browser/searchView'; import { SearchViewlet } from 'vs/workbench/contrib/search/browser/searchViewlet'; @@ -623,6 +623,13 @@ registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleSearchOnTypeA registry.registerWorkbenchAction(SyncActionDescriptor.create(RefreshAction, RefreshAction.ID, RefreshAction.LABEL), 'Search: Refresh', category); registry.registerWorkbenchAction(SyncActionDescriptor.create(ClearSearchResultsAction, ClearSearchResultsAction.ID, ClearSearchResultsAction.LABEL), 'Search: Clear Search Results', category); +registry.registerWorkbenchAction( + SyncActionDescriptor.create(OpenResultsInEditorAction, OpenResultsInEditorAction.ID, OpenResultsInEditorAction.LABEL, + { primary: KeyMod.CtrlCmd | KeyCode.Enter }, + ContextKeyExpr.and(Constants.HasSearchResults, Constants.SearchViewFocusedKey, Constants.EnableSearchEditorPreview)), + 'Search: Open Results in Editor', category, + ContextKeyExpr.and(Constants.EnableSearchEditorPreview)); + // Register Quick Open Handler Registry.as(QuickOpenExtensions.Quickopen).registerDefaultQuickOpenHandler( diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 8cb3ff896be..b4dbbec31c1 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -23,12 +23,13 @@ import { FolderMatch, FileMatch, FileMatchOrMatch, FolderMatchWithResource, Matc import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { ISearchConfiguration, VIEWLET_ID, PANEL_ID } from 'vs/workbench/services/search/common/search'; +import { ISearchConfiguration, VIEWLET_ID, PANEL_ID, ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; import { ISearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { SearchViewlet } from 'vs/workbench/contrib/search/browser/searchViewlet'; import { SearchPanel } from 'vs/workbench/contrib/search/browser/searchPanel'; import { ITreeNavigator } from 'vs/base/browser/ui/tree/tree'; +import { createEditorFromSearchResult } from 'vs/workbench/contrib/search/browser/searchEditor'; export function isSearchViewFocused(viewletService: IViewletService, panelService: IPanelService): boolean { const searchView = getSearchView(viewletService, panelService); @@ -418,6 +419,39 @@ export class CancelSearchAction extends Action { } } +export class OpenResultsInEditorAction extends Action { + + static readonly ID: string = Constants.OpenInEditorCommandId; + static readonly LABEL = nls.localize('search.openResultsInEditor', "Open Results in Editor"); + + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService, + @ILabelService private labelService: ILabelService, + @IEditorService private editorService: IEditorService, + @IConfigurationService private configurationService: IConfigurationService + ) { + super(id, label, 'codicon-go-to-file'); + } + + get enabled(): boolean { + const searchView = getSearchView(this.viewletService, this.panelService); + return !!searchView && searchView.hasSearchResults(); + } + + update() { + this._setEnabled(this.enabled); + } + + async run() { + const searchView = getSearchView(this.viewletService, this.panelService); + if (searchView && this.configurationService.getValue('search').enableSearchEditorPreview) { + await createEditorFromSearchResult(searchView.searchResult, searchView.searchIncludePattern.getValue(), searchView.searchExcludePattern.getValue(), this.labelService, this.editorService); + } + } +} + + export class FocusNextSearchResultAction extends Action { static readonly ID = 'search.action.focusNextSearchResult'; static readonly LABEL = nls.localize('FocusNextSearchResult.label', "Focus Next Search Result"); diff --git a/src/vs/workbench/contrib/search/browser/searchEditor.ts b/src/vs/workbench/contrib/search/browser/searchEditor.ts new file mode 100644 index 00000000000..461040b475f --- /dev/null +++ b/src/vs/workbench/contrib/search/browser/searchEditor.ts @@ -0,0 +1,158 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Match, searchMatchComparer, FileMatch, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; +import { repeat } from 'vs/base/common/strings'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { coalesce, flatten } from 'vs/base/common/arrays'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { URI } from 'vs/base/common/uri'; +import { ITextQuery } from 'vs/workbench/services/search/common/search'; +import * as network from 'vs/base/common/network'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; + +// Using \r\n on Windows inserts an extra newline between results. +const lineDelimiter = '\n'; + +const translateRangeLines = (n: number) => (range: Range) => new Range(range.startLineNumber + n, range.startColumn, range.endLineNumber + n, range.endColumn); + +type SearchResultSerialization = { text: string[], matchRanges: Range[] }; + +function matchToSearchResultFormat(match: Match): { line: string, ranges: Range[], lineNumber: string }[] { + const getLinePrefix = (i: number) => `${match.range().startLineNumber + i}`; + + const fullMatchLines = match.fullPreviewLines(); + const largestPrefixSize = fullMatchLines.reduce((largest, _, i) => Math.max(getLinePrefix(i).length, largest), 0); + + + const results: { line: string, ranges: Range[], lineNumber: string }[] = []; + + fullMatchLines + .forEach((sourceLine, i) => { + const lineNumber = getLinePrefix(i); + const paddingStr = repeat(' ', largestPrefixSize - lineNumber.length); + const prefix = ` ${lineNumber}: ${paddingStr}`; + const prefixOffset = prefix.length; + + const line = (prefix + sourceLine); + + const rangeOnThisLine = ({ start, end }: { start?: number; end?: number; }) => new Range(1, (start ?? 1) + prefixOffset, 1, (end ?? sourceLine.length + 1) + prefixOffset); + + const matchRange = match.range(); + const matchIsSingleLine = matchRange.startLineNumber === matchRange.endLineNumber; + + let lineRange; + if (matchIsSingleLine) { lineRange = (rangeOnThisLine({ start: matchRange.startColumn, end: matchRange.endColumn })); } + else if (i === 0) { lineRange = (rangeOnThisLine({ start: matchRange.startColumn })); } + else if (i === fullMatchLines.length - 1) { lineRange = (rangeOnThisLine({ end: matchRange.endColumn })); } + else { lineRange = (rangeOnThisLine({})); } + + results.push({ lineNumber: lineNumber, line, ranges: [lineRange] }); + }); + + return results; +} + +function fileMatchToSearchResultFormat(fileMatch: FileMatch, labelFormatter: (x: URI) => string): SearchResultSerialization { + const serializedMatches = flatten(fileMatch.matches() + .sort(searchMatchComparer) + .map(match => matchToSearchResultFormat(match))); + + const uriString = labelFormatter(fileMatch.resource); + let text: string[] = [`${uriString}:`]; + let matchRanges: Range[] = []; + + const targetLineNumberToOffset: Record = {}; + + const seenLines = new Set(); + serializedMatches.forEach(match => { + if (!seenLines.has(match.line)) { + targetLineNumberToOffset[match.lineNumber] = text.length; + seenLines.add(match.line); + text.push(match.line); + } + + matchRanges.push(...match.ranges.map(translateRangeLines(targetLineNumberToOffset[match.lineNumber]))); + }); + + + return { text, matchRanges }; +} + +const flattenSearchResultSerializations = (serializations: SearchResultSerialization[]): SearchResultSerialization => { + let text: string[] = []; + let matchRanges: Range[] = []; + + serializations.forEach(serialized => { + serialized.matchRanges.map(translateRangeLines(text.length)).forEach(range => matchRanges.push(range)); + serialized.text.forEach(line => text.push(line)); + text.push(''); // new line + }); + + return { text, matchRanges }; +}; + +function contentPatternToSearchResultHeader(pattern: ITextQuery | null, includes: string, excludes: string): string[] { + if (!pattern) { return []; } + + const removeNullFalseAndUndefined = (a: (T | null | false | undefined)[]) => a.filter(a => a !== false && a !== null && a !== undefined) as T[]; + + const escapeNewlines = (str: string) => str.replace(/\\/g, '\\\\').replace(/\n/g, '\\n'); + + return removeNullFalseAndUndefined([ + `# Query: ${escapeNewlines(pattern.contentPattern.pattern)}`, + + (pattern.contentPattern.isCaseSensitive || pattern.contentPattern.isWordMatch || pattern.contentPattern.isRegExp) + && `# Flags: ${coalesce([ + pattern.contentPattern.isCaseSensitive && 'CaseSensitive', + pattern.contentPattern.isWordMatch && 'WordMatch', + pattern.contentPattern.isRegExp && 'RegExp' + ]).join(' ')}`, + includes ? `# Including: ${includes}` : undefined, + excludes ? `# Excluding: ${excludes}` : undefined, + '' + ]); +} + +const serializeSearchResultForEditor = (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, labelFormatter: (x: URI) => string): SearchResultSerialization => { + const header = contentPatternToSearchResultHeader(searchResult.query, rawIncludePattern, rawExcludePattern); + const allResults = + flattenSearchResultSerializations( + flatten(searchResult.folderMatches() + .map(folderMatch => folderMatch.matches() + .map(fileMatch => fileMatchToSearchResultFormat(fileMatch, labelFormatter))))); + + return { matchRanges: allResults.matchRanges.map(translateRangeLines(header.length)), text: header.concat(allResults.text) }; +}; + +export const createEditorFromSearchResult = + async (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, labelService: ILabelService, editorService: IEditorService) => { + const searchTerm = searchResult.query?.contentPattern.pattern.replace(/[^\w-_.]/g, '') || 'Search'; + + const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true }); + + const results = serializeSearchResultForEditor(searchResult, rawIncludePattern, rawExcludePattern, labelFormatter); + + let possible = { + contents: results.text.join(lineDelimiter), + mode: 'search-result', + resource: URI.from({ scheme: network.Schemas.untitled, path: searchTerm }) + }; + + let id = 0; + while (editorService.getOpened(possible)) { + possible.resource = possible.resource.with({ path: searchTerm + '-' + ++id }); + } + + const editor = await editorService.openEditor(possible); + const control = editor?.getControl()!; + control.updateOptions({ lineNumbers: 'off' }); + + const model = control.getModel() as ITextModel; + + model.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'findMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); + + }; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 2fc338d2dab..cb734ff0e65 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -43,7 +43,7 @@ import { OpenFileFolderAction, OpenFolderAction } from 'vs/workbench/browser/act import { ResourceLabels } from 'vs/workbench/browser/labels'; import { IEditor } from 'vs/workbench/common/editor'; import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget'; -import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, RefreshAction, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions'; +import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, RefreshAction, IFindInFilesArgs, OpenResultsInEditorAction, appendKeyBindingLabel } from 'vs/workbench/contrib/search/browser/searchActions'; import { FileMatchRenderer, FolderMatchRenderer, MatchRenderer, SearchAccessibilityProvider, SearchDelegate, SearchDND } from 'vs/workbench/contrib/search/browser/searchResultsView'; import { ISearchWidgetOptions, SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; @@ -62,6 +62,8 @@ import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; +import { createEditorFromSearchResult } from 'vs/workbench/contrib/search/browser/searchEditor'; +import { ILabelService } from 'vs/platform/label/common/label'; const $ = dom.$; @@ -105,10 +107,11 @@ export class SearchView extends ViewletPane { private folderMatchFocused: IContextKey; private matchFocused: IContextKey; private hasSearchResultsKey: IContextKey; + private enableSearchEditorPreview: IContextKey; private state: SearchUIState = SearchUIState.Idle; - private actions: Array = []; + private actions: Array = []; private cancelAction: CancelSearchAction; private refreshAction: RefreshAction; private contextMenu: IMenu | null = null; @@ -161,6 +164,7 @@ export class SearchView extends ViewletPane { @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @IKeybindingService keybindingService: IKeybindingService, @IStorageService storageService: IStorageService, + @ILabelService private readonly labelService: ILabelService, @IOpenerService private readonly openerService: IOpenerService ) { super({ ...options, id: VIEW_ID, ariaHeaderLabel: nls.localize('searchView', "Search") }, keybindingService, contextMenuService, configurationService, contextKeyService); @@ -178,6 +182,14 @@ export class SearchView extends ViewletPane { this.folderMatchFocused = Constants.FolderFocusKey.bindTo(contextKeyService); this.matchFocused = Constants.MatchFocusKey.bindTo(this.contextKeyService); this.hasSearchResultsKey = Constants.HasSearchResults.bindTo(this.contextKeyService); + this.enableSearchEditorPreview = Constants.EnableSearchEditorPreview.bindTo(this.contextKeyService); + + this.enableSearchEditorPreview.set(this.searchConfig.enableSearchEditorPreview); + this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('search.previewSearchEditor')) { + this.enableSearchEditorPreview.set(this.searchConfig.enableSearchEditorPreview); + } + }); this.viewModel = this._register(this.searchWorkbenchService.searchModel); this.queryBuilder = this.instantiationService.createInstance(QueryBuilder); @@ -197,6 +209,13 @@ export class SearchView extends ViewletPane { this._register(this.instantiationService.createInstance(ClearSearchResultsAction, ClearSearchResultsAction.ID, ClearSearchResultsAction.LABEL)), this._register(this.instantiationService.createInstance(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL)) ]; + + if (this.searchConfig.enableSearchEditorPreview) { + this.actions.push( + this._register(this.instantiationService.createInstance(OpenResultsInEditorAction, OpenResultsInEditorAction.ID, OpenResultsInEditorAction.LABEL)) + ); + } + this.refreshAction = this._register(this.instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL)); this.cancelAction = this._register(this.instantiationService.createInstance(CancelSearchAction, CancelSearchAction.ID, CancelSearchAction.LABEL)); } @@ -458,7 +477,7 @@ export class SearchView extends ViewletPane { } refreshTree(event?: IChangeEvent): void { - const collapseResults = this.configurationService.getValue('search').collapseResults; + const collapseResults = this.searchConfig.collapseResults; if (!event || event.added || event.removed) { // Refresh whole tree this.tree.setChildren(null, this.createResultIterator(collapseResults)); @@ -892,7 +911,7 @@ export class SearchView extends ViewletPane { return; } - const actionsPosition = this.configurationService.getValue('search').actionsPosition; + const actionsPosition = this.searchConfig.actionsPosition; dom.toggleClass(this.getContainer(), SearchView.ACTIONS_RIGHT_CLASS_NAME, actionsPosition === 'right'); dom.toggleClass(this.getContainer(), SearchView.WIDE_CLASS_NAME, this.size.width >= SearchView.WIDE_VIEW_SIZE); @@ -1197,8 +1216,7 @@ export class SearchView extends ViewletPane { // Need the full match line to correctly calculate replace text, if this is a search/replace with regex group references ($1, $2, ...). // 10000 chars is enough to avoid sending huge amounts of text around, if you do a replace with a longer match, it may or may not resolve the group refs correctly. // https://github.com/Microsoft/vscode/issues/58374 - const charsPerLine = content.isRegExp ? 10000 : - 250; + const charsPerLine = content.isRegExp ? 10000 : 1000; const options: ITextQueryBuilderOptions = { _reason: 'searchView', @@ -1212,7 +1230,7 @@ export class SearchView extends ViewletPane { matchLines: 1, charsPerLine }, - isSmartCase: this.configurationService.getValue().search.smartCase, + isSmartCase: this.searchConfig.smartCase, expandPatterns: true }; const folderResources = this.contextService.getWorkspace().folders; @@ -1298,7 +1316,7 @@ export class SearchView extends ViewletPane { // Do final render, then expand if just 1 file with less than 50 matches this.onSearchResultsChanged(); - const collapseResults = this.configurationService.getValue('search').collapseResults; + const collapseResults = this.searchConfig.collapseResults; if (collapseResults !== 'alwaysCollapse' && this.viewModel.searchResult.matches().length === 1) { const onlyMatch = this.viewModel.searchResult.matches()[0]; if (onlyMatch.count() < 50) { @@ -1476,7 +1494,23 @@ export class SearchView extends ViewletPane { resultMsg += nls.localize('useIgnoresAndExcludesDisabled', " - exclude settings and ignore files are disabled"); } - dom.append(messageEl, $('p', undefined, resultMsg)); + if (this.searchConfig.enableSearchEditorPreview) { + dom.append(messageEl, $('span', undefined, resultMsg + ' - ')); + const span = dom.append(messageEl, $('span', undefined)); + const openInEditorLink = dom.append(span, $('a.pointer.prominent', undefined, nls.localize('openInEditor.message', "Open in editor"))); + + openInEditorLink.title = appendKeyBindingLabel( + nls.localize('openInEditor.tooltip', "Copy current search results to an editor"), + this.keybindingService.lookupKeybinding(Constants.OpenInEditorCommandId), this.keybindingService); + + this.messageDisposables.push(dom.addDisposableListener(openInEditorLink, dom.EventType.CLICK, (e: MouseEvent) => { + dom.EventHelper.stop(e, false); + createEditorFromSearchResult(this.searchResult, this.searchIncludePattern.getValue(), this.searchExcludePattern.getValue(), this.labelService, this.editorService); + })); + + } else { + dom.append(messageEl, $('p', undefined, resultMsg)); + } this.reLayout(); } else if (!msgWasHidden) { dom.hide(this.messagesElement); @@ -1625,6 +1659,10 @@ export class SearchView extends ViewletPane { ]; } + private get searchConfig(): ISearchConfigurationProperties { + return this.configurationService.getValue('search'); + } + private clearHistory(): void { this.searchWidget.clearHistory(); this.inputPatternExcludes.clearHistory(); diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index d2261e85097..2b5ff708245 100644 --- a/src/vs/workbench/contrib/search/common/constants.ts +++ b/src/vs/workbench/contrib/search/common/constants.ts @@ -15,6 +15,7 @@ export const RemoveActionId = 'search.action.remove'; export const CopyPathCommandId = 'search.action.copyPath'; export const CopyMatchCommandId = 'search.action.copyMatch'; export const CopyAllCommandId = 'search.action.copyAll'; +export const OpenInEditorCommandId = 'search.action.openInEditor'; export const ClearSearchHistoryCommandId = 'search.action.clearHistory'; export const FocusSearchListCommandID = 'search.action.focusSearchList'; export const ReplaceActionId = 'search.action.replace'; @@ -37,6 +38,7 @@ export const PatternIncludesFocusedKey = new RawContextKey('patternIncl export const PatternExcludesFocusedKey = new RawContextKey('patternExcludesInputBoxFocus', false); export const ReplaceActiveKey = new RawContextKey('replaceActive', false); export const HasSearchResults = new RawContextKey('hasSearchResult', false); +export const EnableSearchEditorPreview = new RawContextKey('previewSearchEditor', false); export const FirstMatchFocusKey = new RawContextKey('firstMatchFocus', false); export const FileMatchOrMatchFocusKey = new RawContextKey('fileMatchOrMatchFocus', false); // This is actually, Match or File or Folder diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index cdd195c17e8..18ce0750f09 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -965,6 +965,12 @@ export class SearchModel extends Disposable { search(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): Promise { this.cancelSearch(); + // Exclude Search Editor results unless explicity included + const searchEditorFilenameGlob = `**/*.code-search`; + if (!query.includePattern || !query.includePattern[searchEditorFilenameGlob]) { + query.excludePattern = { ...(query.excludePattern ?? {}), [searchEditorFilenameGlob]: true }; + } + this._searchQuery = query; if (!this.searchConfig.searchOnType) { this.searchResult.clear(); diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 57fd425f6e6..9c842761a68 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -331,6 +331,8 @@ export interface ISearchConfigurationProperties { collapseResults: 'auto' | 'alwaysCollapse' | 'alwaysExpand'; searchOnType: boolean; searchOnTypeDebouncePeriod: number; + enableSearchEditorPreview: boolean; + searchEditorPreviewForceAbsolutePaths: boolean; } export interface ISearchConfiguration extends IFilesConfiguration { diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index b9c252c2ca0..ddbe6ecb49e 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -391,6 +391,12 @@ export class SearchService extends Disposable implements ISearchService { return; } + // Skip search results + if (model.getModeId() === 'search-result' && !(query.includePattern && query.includePattern['**/*.code-search'])) { + // TODO: untitled search editors will be excluded from search even when include *.code-search is specified + return; + } + // Support untitled files if (resource.scheme === Schemas.untitled) { if (!this.untitledTextEditorService.exists(resource)) { From ce1173d41e7145aa66fd2197fe93757b4e2b75f0 Mon Sep 17 00:00:00 2001 From: Nilesh Date: Sun, 24 Nov 2019 17:03:29 +0530 Subject: [PATCH 08/58] Used FileMathOrMatch for putting multicursor for search result --- .../editor/contrib/multicursor/multicursor.ts | 14 +++-------- .../contrib/search/browser/searchView.ts | 23 ++++++++++++------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index ee80be6a2c2..f2e861e36c8 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -632,17 +632,9 @@ export class MultiCursorSelectionController extends Disposable implements IEdito } } - public selectAllUsingString(searchString: string, isRegex: boolean, matchCase: boolean, wholeWord: boolean, limitResultCount?: number): void { - if (!this._editor.hasModel()) { - return; - } - - let matches: FindMatch[] | null = null; - - matches = this._editor.getModel().findMatches(searchString, true, isRegex, matchCase, wholeWord ? this._editor.getOption(EditorOption.wordSeparators) : null, false, Constants.MAX_SAFE_SMALL_INTEGER); - - if (matches.length > 0) { - this._setSelections(matches.map(m => new Selection(m.range.startLineNumber, m.range.startColumn, m.range.endLineNumber, m.range.endColumn))); + public selectAllUsingSelections(selections: Selection[]): void { + if (selections.length > 0) { + this._setSelections(selections); } } } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 4df1c50ebef..46292c294f9 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -62,6 +62,7 @@ import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { MultiCursorSelectionController } from 'vs/editor/contrib/multicursor/multicursor'; +import { Selection } from 'vs/editor/common/core/selection'; const $ = dom.$; @@ -1561,15 +1562,21 @@ export class SearchView extends ViewletPanel { } }).then(editor => { if (editor) { - let codeEditor = getCodeEditor(editor.getControl()); - if (codeEditor) { - let multiCursorController = MultiCursorSelectionController.get(codeEditor); - let isRegex = this.searchWidget.searchInput.getRegex(); - let isWholeWords = this.searchWidget.searchInput.getWholeWords(); - let isCaseSensitive = this.searchWidget.searchInput.getCaseSensitive(); - let contentPattern = this.searchWidget.searchInput.getValue(); + let fileMatch = null; + if (element instanceof FileMatch) { + fileMatch = element; + } + else if (element instanceof Match) { + fileMatch = element.parent(); + } - multiCursorController.selectAllUsingString(contentPattern, isRegex, isCaseSensitive, isWholeWords); + if (fileMatch) { + const selections = fileMatch.matches().map(m => new Selection(m.range().startLineNumber, m.range().startColumn, m.range().endLineNumber, m.range().endColumn)); + const codeEditor = getCodeEditor(editor.getControl()); + if (codeEditor) { + let multiCursorController = MultiCursorSelectionController.get(codeEditor); + multiCursorController.selectAllUsingSelections(selections); + } } } this.viewModel.searchResult.rangeHighlightDecorations.removeHighlightRange(); From 3e3cb3043cd5c397333ffe7ae518275a32014cfb Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 24 Nov 2019 09:48:27 -0800 Subject: [PATCH 09/58] xterm-addon-webgl@0.4.0-beta.11 Fixes #85048 --- package.json | 2 +- remote/package.json | 2 +- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 ++++---- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 1a035661007..77828e463db 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "xterm": "4.3.0-beta24", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", - "xterm-addon-webgl": "0.4.0-beta9", + "xterm-addon-webgl": "0.4.0-beta.11", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 9823a4c0338..fbcfa995a5e 100644 --- a/remote/package.json +++ b/remote/package.json @@ -23,7 +23,7 @@ "xterm": "4.3.0-beta24", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", - "xterm-addon-webgl": "0.4.0-beta9", + "xterm-addon-webgl": "0.4.0-beta.11", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index d2dd3026cb2..9e7c95ebfee 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -8,6 +8,6 @@ "xterm": "4.3.0-beta24", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", - "xterm-addon-webgl": "0.4.0-beta9" + "xterm-addon-webgl": "0.4.0-beta.11" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 6e26754170b..968aa5062b1 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -41,10 +41,10 @@ xterm-addon-web-links@0.2.1: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2" integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ== -xterm-addon-webgl@0.4.0-beta9: - version "0.4.0-beta9" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta9.tgz#3e004d5cd893ae678e2537b195ae0eed4d689df3" - integrity sha512-+lUsUAx4ATyetRuTuEorUpKD5NpDUUc5Z3chtYV8ECiTJYiDr0CfAxW9oa3tT8BVO7fOTdgxgJpQmsU4LGEm5A== +xterm-addon-webgl@0.4.0-beta.11: + version "0.4.0-beta.11" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta.11.tgz#0e4a7242e2353cf74aba55e5a5bdc0b4ec87ad10" + integrity sha512-AteDxm1RFy1WnjY9r5iJSETozLebvUkR+jextdZk/ASsK21vsYK0DuVWwRI8afgiN2hUVhxcxuHEJUOV+CJDQA== xterm@4.3.0-beta24: version "4.3.0-beta24" diff --git a/remote/yarn.lock b/remote/yarn.lock index bc473844c5f..4091caaa94d 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -428,10 +428,10 @@ xterm-addon-web-links@0.2.1: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2" integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ== -xterm-addon-webgl@0.4.0-beta9: - version "0.4.0-beta9" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta9.tgz#3e004d5cd893ae678e2537b195ae0eed4d689df3" - integrity sha512-+lUsUAx4ATyetRuTuEorUpKD5NpDUUc5Z3chtYV8ECiTJYiDr0CfAxW9oa3tT8BVO7fOTdgxgJpQmsU4LGEm5A== +xterm-addon-webgl@0.4.0-beta.11: + version "0.4.0-beta.11" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta.11.tgz#0e4a7242e2353cf74aba55e5a5bdc0b4ec87ad10" + integrity sha512-AteDxm1RFy1WnjY9r5iJSETozLebvUkR+jextdZk/ASsK21vsYK0DuVWwRI8afgiN2hUVhxcxuHEJUOV+CJDQA== xterm@4.3.0-beta24: version "4.3.0-beta24" diff --git a/yarn.lock b/yarn.lock index fde045af196..2554077d820 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9369,10 +9369,10 @@ xterm-addon-web-links@0.2.1: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2" integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ== -xterm-addon-webgl@0.4.0-beta9: - version "0.4.0-beta9" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta9.tgz#3e004d5cd893ae678e2537b195ae0eed4d689df3" - integrity sha512-+lUsUAx4ATyetRuTuEorUpKD5NpDUUc5Z3chtYV8ECiTJYiDr0CfAxW9oa3tT8BVO7fOTdgxgJpQmsU4LGEm5A== +xterm-addon-webgl@0.4.0-beta.11: + version "0.4.0-beta.11" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta.11.tgz#0e4a7242e2353cf74aba55e5a5bdc0b4ec87ad10" + integrity sha512-AteDxm1RFy1WnjY9r5iJSETozLebvUkR+jextdZk/ASsK21vsYK0DuVWwRI8afgiN2hUVhxcxuHEJUOV+CJDQA== xterm@4.3.0-beta24: version "4.3.0-beta24" From 958853a7cfd991a39c043db44411c6fc2c2ad7ad Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 24 Nov 2019 11:31:14 -0800 Subject: [PATCH 10/58] xterm@4.3.0-beta.28 Diff: https://github.com/xtermjs/xterm.js/compare/1300b7a...397d053 Fixes #84299 --- package.json | 2 +- remote/package.json | 2 +- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 ++++---- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 77828e463db..c37914c63dc 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "vscode-ripgrep": "^1.5.7", "vscode-sqlite3": "4.0.9", "vscode-textmate": "4.4.0", - "xterm": "4.3.0-beta24", + "xterm": "4.3.0-beta.28", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", "xterm-addon-webgl": "0.4.0-beta.11", diff --git a/remote/package.json b/remote/package.json index fbcfa995a5e..216a7c37015 100644 --- a/remote/package.json +++ b/remote/package.json @@ -20,7 +20,7 @@ "vscode-proxy-agent": "^0.5.2", "vscode-ripgrep": "^1.5.7", "vscode-textmate": "4.4.0", - "xterm": "4.3.0-beta24", + "xterm": "4.3.0-beta.28", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", "xterm-addon-webgl": "0.4.0-beta.11", diff --git a/remote/web/package.json b/remote/web/package.json index 9e7c95ebfee..2a0519d282d 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -5,7 +5,7 @@ "onigasm-umd": "2.2.5", "semver-umd": "^5.5.3", "vscode-textmate": "4.4.0", - "xterm": "4.3.0-beta24", + "xterm": "4.3.0-beta.28", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", "xterm-addon-webgl": "0.4.0-beta.11" diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 968aa5062b1..1e2d3f0de21 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -46,7 +46,7 @@ xterm-addon-webgl@0.4.0-beta.11: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta.11.tgz#0e4a7242e2353cf74aba55e5a5bdc0b4ec87ad10" integrity sha512-AteDxm1RFy1WnjY9r5iJSETozLebvUkR+jextdZk/ASsK21vsYK0DuVWwRI8afgiN2hUVhxcxuHEJUOV+CJDQA== -xterm@4.3.0-beta24: - version "4.3.0-beta24" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta24.tgz#a78684ac1d1bd263f3086ec4190d00cf9ce51fb9" - integrity sha512-N6slYV/c02hxTVgh21JvphBKkMTdvzljqFM01Inx9rriO4rVZEn39ZX/Sfer9Qm+vlgCvMtOm4pn0bCsK2OZdg== +xterm@4.3.0-beta.28: + version "4.3.0-beta.28" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta.28.tgz#80f7c4ba8f6ee3c953e6f33f8ce5aef08d5a8354" + integrity sha512-WWZ4XCvce5h+klL6ObwtMauJff/n2KGGOwJJkDbJhrAjVy2a77GKgAedJTDDFGgKJ6ix1d7puHtVSSKflIVaDQ== diff --git a/remote/yarn.lock b/remote/yarn.lock index 4091caaa94d..6b661b17e47 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -433,10 +433,10 @@ xterm-addon-webgl@0.4.0-beta.11: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta.11.tgz#0e4a7242e2353cf74aba55e5a5bdc0b4ec87ad10" integrity sha512-AteDxm1RFy1WnjY9r5iJSETozLebvUkR+jextdZk/ASsK21vsYK0DuVWwRI8afgiN2hUVhxcxuHEJUOV+CJDQA== -xterm@4.3.0-beta24: - version "4.3.0-beta24" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta24.tgz#a78684ac1d1bd263f3086ec4190d00cf9ce51fb9" - integrity sha512-N6slYV/c02hxTVgh21JvphBKkMTdvzljqFM01Inx9rriO4rVZEn39ZX/Sfer9Qm+vlgCvMtOm4pn0bCsK2OZdg== +xterm@4.3.0-beta.28: + version "4.3.0-beta.28" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta.28.tgz#80f7c4ba8f6ee3c953e6f33f8ce5aef08d5a8354" + integrity sha512-WWZ4XCvce5h+klL6ObwtMauJff/n2KGGOwJJkDbJhrAjVy2a77GKgAedJTDDFGgKJ6ix1d7puHtVSSKflIVaDQ== yauzl@^2.9.2: version "2.10.0" diff --git a/yarn.lock b/yarn.lock index 2554077d820..a4b936375f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9374,10 +9374,10 @@ xterm-addon-webgl@0.4.0-beta.11: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta.11.tgz#0e4a7242e2353cf74aba55e5a5bdc0b4ec87ad10" integrity sha512-AteDxm1RFy1WnjY9r5iJSETozLebvUkR+jextdZk/ASsK21vsYK0DuVWwRI8afgiN2hUVhxcxuHEJUOV+CJDQA== -xterm@4.3.0-beta24: - version "4.3.0-beta24" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta24.tgz#a78684ac1d1bd263f3086ec4190d00cf9ce51fb9" - integrity sha512-N6slYV/c02hxTVgh21JvphBKkMTdvzljqFM01Inx9rriO4rVZEn39ZX/Sfer9Qm+vlgCvMtOm4pn0bCsK2OZdg== +xterm@4.3.0-beta.28: + version "4.3.0-beta.28" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta.28.tgz#80f7c4ba8f6ee3c953e6f33f8ce5aef08d5a8354" + integrity sha512-WWZ4XCvce5h+klL6ObwtMauJff/n2KGGOwJJkDbJhrAjVy2a77GKgAedJTDDFGgKJ6ix1d7puHtVSSKflIVaDQ== y18n@^3.2.1: version "3.2.1" From 4979ef1a7551e11db13edc535215ce7ef808a502 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 24 Nov 2019 11:34:43 -0800 Subject: [PATCH 11/58] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c37914c63dc..5ee09edfd02 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.41.0", - "distro": "2ec0dcb1b859bdfa9da42cb5243114591fee61af", + "distro": "30dcc7436405dfa68898d0f3843551c589fc9008", "author": { "name": "Microsoft Corporation" }, From d6cd08ba605f52df412cf072440558a13eb5af91 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sun, 24 Nov 2019 12:49:47 -0800 Subject: [PATCH 12/58] Fix settings search input sizing with "clear" action Fix #85161 --- .../contrib/preferences/browser/settingsEditor2.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 159d2baf779..3cdd39a86ed 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -98,6 +98,7 @@ export class SettingsEditor2 extends BaseEditor { private headerContainer!: HTMLElement; private searchWidget!: SuggestEnabledInput; private countElement!: HTMLElement; + private controlsElement!: HTMLElement; private settingsTargetsWidget!: SettingsTargetsWidget; private settingsTreeContainer!: HTMLElement; @@ -301,7 +302,8 @@ export class SettingsEditor2 extends BaseEditor { this.layoutTrees(dimension); const innerWidth = Math.min(1000, dimension.width) - 24 * 2; // 24px padding on left and right; - const monacoWidth = innerWidth - 10 - this.countElement.clientWidth - 12; // minus padding inside inputbox, countElement width, extra padding before countElement + // minus padding inside inputbox, countElement width, controls width, extra padding before countElement + const monacoWidth = innerWidth - 10 - this.countElement.clientWidth - this.controlsElement.clientWidth - 12; this.searchWidget.layout({ height: 20, width: monacoWidth }); DOM.toggleClass(this.rootElement, 'mid-width', dimension.width < 1000 && dimension.width >= 600); @@ -442,9 +444,9 @@ export class SettingsEditor2 extends BaseEditor { this.settingsTargetsWidget.settingsTarget = ConfigurationTarget.USER_LOCAL; this.settingsTargetsWidget.onDidTargetChange(target => this.onDidSettingsTargetChange(target)); - const actionsContainer = DOM.append(searchContainer, DOM.$('.settings-clear-widget')); + this.controlsElement = DOM.append(searchContainer, DOM.$('.settings-clear-widget')); - const actionBar = this._register(new ActionBar(actionsContainer, { + const actionBar = this._register(new ActionBar(this.controlsElement, { animated: false, actionViewItemProvider: (action: Action) => { return undefined; } })); From c41d9dc9b4bc0b43f17146f4f1a8a6c880242749 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Sun, 24 Nov 2019 01:18:06 -0800 Subject: [PATCH 13/58] Remove multi-match peek UX. --- extensions/search-result/src/extension.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index e538bd7fab3..866645fee86 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -18,7 +18,9 @@ export function activate() { const lineResult = parseSearchResults(document, token)[position.line]; if (!lineResult) { return []; } if (lineResult.type === 'file') { - return lineResult.allLocations.length > 1 ? lineResult.allLocations : [lineResult.location]; + // TODO: The multi-match peek UX isnt very smooth. + // return lineResult.allLocations.length > 1 ? lineResult.allLocations : [lineResult.location]; + return []; } return [lineResult.location]; From c2e11d41f5b2000673c81d70fc725af039a032d4 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Sun, 24 Nov 2019 16:47:58 -0800 Subject: [PATCH 14/58] Add re-run search editor search action --- extensions/search-result/package.json | 20 +++ extensions/search-result/package.nls.json | 3 +- extensions/search-result/src/extension.ts | 18 ++- .../search-result/src/media/refresh-dark.svg | 4 + .../search-result/src/media/refresh-light.svg | 4 + .../search/browser/search.contribution.ts | 10 +- .../contrib/search/browser/searchActions.ts | 30 ++++- .../contrib/search/browser/searchEditor.ts | 124 ++++++++++++++++-- .../contrib/search/common/constants.ts | 1 + 9 files changed, 194 insertions(+), 20 deletions(-) create mode 100644 extensions/search-result/src/media/refresh-dark.svg create mode 100644 extensions/search-result/src/media/refresh-light.svg diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json index 3639f83366d..a625733aa8a 100644 --- a/extensions/search-result/package.json +++ b/extensions/search-result/package.json @@ -16,6 +16,26 @@ "*" ], "contributes": { + "commands": [ + { + "command": "searchResult.rerunSearch", + "title": "%searchResult.rerunSearch.title%", + "category": "Search Result", + "icon": { + "light": "./src/media/refresh-light.svg", + "dark": "./src/media/refresh-dark.svg" + } + } + ], + "menus": { + "editor/title": [ + { + "command": "searchResult.rerunSearch", + "when": "editorLangId == search-result", + "group": "navigation" + } + ] + }, "languages": [ { "id": "search-result", diff --git a/extensions/search-result/package.nls.json b/extensions/search-result/package.nls.json index 324fd97bcd2..694f6b61d80 100644 --- a/extensions/search-result/package.nls.json +++ b/extensions/search-result/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Search Result", - "description": "Provides syntax highlighting and language features for tabbed search results." + "description": "Provides syntax highlighting and language features for tabbed search results.", + "searchResult.rerunSearch.title": "Search Again" } diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index 866645fee86..aa4090cc50e 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -8,12 +8,26 @@ import * as pathUtils from 'path'; const FILE_LINE_REGEX = /^(\S.*):$/; const RESULT_LINE_REGEX = /^(\s+)(\d+):(\s+)(.*)$/; +const LANGUAGE_SELECTOR = { language: 'search-result' }; let cachedLastParse: { version: number, parse: ParsedSearchResults } | undefined; export function activate() { - vscode.languages.registerDefinitionProvider({ language: 'search-result' }, { + vscode.commands.registerCommand('searchResult.rerunSearch', () => vscode.commands.executeCommand('search.action.rerunEditorSearch')); + + vscode.languages.registerCompletionItemProvider(LANGUAGE_SELECTOR, { + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] { + const line = document.lineAt(position.line); + if (line.text.indexOf('# Flags:') === -1) { return []; } + + return ['RegExp', 'CaseSensitive', 'IgnoreExcludeSettings', 'WordMatch'] + .filter(flag => line.text.indexOf(flag) === -1) + .map(flag => ({ label: flag, insertText: flag + ' ' })); + } + }); + + vscode.languages.registerDefinitionProvider(LANGUAGE_SELECTOR, { provideDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.DefinitionLink[] { const lineResult = parseSearchResults(document, token)[position.line]; if (!lineResult) { return []; } @@ -27,7 +41,7 @@ export function activate() { } }); - vscode.languages.registerDocumentLinkProvider({ language: 'search-result' }, { + vscode.languages.registerDocumentLinkProvider(LANGUAGE_SELECTOR, { async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { return parseSearchResults(document, token) .filter(({ type }) => type === 'file') diff --git a/extensions/search-result/src/media/refresh-dark.svg b/extensions/search-result/src/media/refresh-dark.svg new file mode 100644 index 00000000000..e1f05aadeeb --- /dev/null +++ b/extensions/search-result/src/media/refresh-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/extensions/search-result/src/media/refresh-light.svg b/extensions/search-result/src/media/refresh-light.svg new file mode 100644 index 00000000000..9b1d9108409 --- /dev/null +++ b/extensions/search-result/src/media/refresh-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 94180385cbb..55fe302365c 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -41,7 +41,7 @@ import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition import { OpenAnythingHandler } from 'vs/workbench/contrib/search/browser/openAnythingHandler'; import { OpenSymbolHandler } from 'vs/workbench/contrib/search/browser/openSymbolHandler'; import { registerContributions as replaceContributions } from 'vs/workbench/contrib/search/browser/replaceContributions'; -import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction, OpenResultsInEditorAction } from 'vs/workbench/contrib/search/browser/searchActions'; +import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction, OpenResultsInEditorAction, RerunEditorSearchAction } from 'vs/workbench/contrib/search/browser/searchActions'; import { SearchPanel } from 'vs/workbench/contrib/search/browser/searchPanel'; import { SearchView, SearchViewPosition } from 'vs/workbench/contrib/search/browser/searchView'; import { SearchViewlet } from 'vs/workbench/contrib/search/browser/searchViewlet'; @@ -56,6 +56,7 @@ import { ISearchConfiguration, ISearchConfigurationProperties, PANEL_ID, VIEWLET import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ExplorerViewlet } from 'vs/workbench/contrib/files/browser/explorerViewlet'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); registerSingleton(ISearchHistoryService, SearchHistoryService, true); @@ -630,6 +631,13 @@ registry.registerWorkbenchAction( 'Search: Open Results in Editor', category, ContextKeyExpr.and(Constants.EnableSearchEditorPreview)); +registry.registerWorkbenchAction( + SyncActionDescriptor.create(RerunEditorSearchAction, RerunEditorSearchAction.ID, RerunEditorSearchAction.LABEL, + { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.KEY_R }, + ContextKeyExpr.and(EditorContextKeys.languageId.isEqualTo('search-result'))), + 'Search Editor: Search Again', category, + ContextKeyExpr.and(EditorContextKeys.languageId.isEqualTo('search-result'))); + // Register Quick Open Handler Registry.as(QuickOpenExtensions.Quickopen).registerDefaultQuickOpenHandler( diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index b4dbbec31c1..c228d3a1218 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -13,7 +13,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { ILabelService } from 'vs/platform/label/common/label'; import { ICommandHandler } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { getSelectionKeyboardEvent, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; @@ -29,7 +29,9 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { SearchViewlet } from 'vs/workbench/contrib/search/browser/searchViewlet'; import { SearchPanel } from 'vs/workbench/contrib/search/browser/searchPanel'; import { ITreeNavigator } from 'vs/base/browser/ui/tree/tree'; -import { createEditorFromSearchResult } from 'vs/workbench/contrib/search/browser/searchEditor'; +import { createEditorFromSearchResult, refreshActiveEditorSearch } from 'vs/workbench/contrib/search/browser/searchEditor'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; export function isSearchViewFocused(viewletService: IViewletService, panelService: IPanelService): boolean { const searchView = getSearchView(viewletService, panelService); @@ -451,6 +453,30 @@ export class OpenResultsInEditorAction extends Action { } } +export class RerunEditorSearchAction extends Action { + + static readonly ID: string = Constants.RerunEditorSearchCommandId; + static readonly LABEL = nls.localize('search.rerunEditorSearch', "Search Again"); + + constructor(id: string, label: string, + @IInstantiationService private instantiationService: IInstantiationService, + @IEditorService private editorService: IEditorService, + @IConfigurationService private configurationService: IConfigurationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @ILabelService private labelService: ILabelService, + @IProgressService private progressService: IProgressService + ) { + super(id, label); + } + + async run() { + if (this.configurationService.getValue('search').enableSearchEditorPreview) { + await this.progressService.withProgress({ location: ProgressLocation.Window }, + () => refreshActiveEditorSearch(this.editorService, this.instantiationService, this.contextService, this.labelService, this.configurationService)); + } + } +} + export class FocusNextSearchResultAction extends Action { static readonly ID = 'search.action.focusNextSearchResult'; diff --git a/src/vs/workbench/contrib/search/browser/searchEditor.ts b/src/vs/workbench/contrib/search/browser/searchEditor.ts index 461040b475f..5c78887001c 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditor.ts @@ -3,25 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Match, searchMatchComparer, FileMatch, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; +import { Match, searchMatchComparer, FileMatch, SearchResult, SearchModel } from 'vs/workbench/contrib/search/common/searchModel'; import { repeat } from 'vs/base/common/strings'; import { ILabelService } from 'vs/platform/label/common/label'; import { coalesce, flatten } from 'vs/base/common/arrays'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { URI } from 'vs/base/common/uri'; -import { ITextQuery } from 'vs/workbench/services/search/common/search'; +import { ITextQuery, IPatternInfo, ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; import * as network from 'vs/base/common/network'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; +import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; // Using \r\n on Windows inserts an extra newline between results. const lineDelimiter = '\n'; -const translateRangeLines = (n: number) => (range: Range) => new Range(range.startLineNumber + n, range.startColumn, range.endLineNumber + n, range.endColumn); +const translateRangeLines = + (n: number) => + (range: Range) => + new Range(range.startLineNumber + n, range.startColumn, range.endLineNumber + n, range.endColumn); -type SearchResultSerialization = { text: string[], matchRanges: Range[] }; - -function matchToSearchResultFormat(match: Match): { line: string, ranges: Range[], lineNumber: string }[] { +const matchToSearchResultFormat = (match: Match): { line: string, ranges: Range[], lineNumber: string }[] => { const getLinePrefix = (i: number) => `${match.range().startLineNumber + i}`; const fullMatchLines = match.fullPreviewLines(); @@ -54,8 +60,9 @@ function matchToSearchResultFormat(match: Match): { line: string, ranges: Range[ }); return results; -} +}; +type SearchResultSerialization = { text: string[], matchRanges: Range[] }; function fileMatchToSearchResultFormat(fileMatch: FileMatch, labelFormatter: (x: URI) => string): SearchResultSerialization { const serializedMatches = flatten(fileMatch.matches() .sort(searchMatchComparer) @@ -95,7 +102,7 @@ const flattenSearchResultSerializations = (serializations: SearchResultSerializa return { text, matchRanges }; }; -function contentPatternToSearchResultHeader(pattern: ITextQuery | null, includes: string, excludes: string): string[] { +const contentPatternToSearchResultHeader = (pattern: ITextQuery | null, includes: string, excludes: string): string[] => { if (!pattern) { return []; } const removeNullFalseAndUndefined = (a: (T | null | false | undefined)[]) => a.filter(a => a !== false && a !== null && a !== undefined) as T[]; @@ -105,29 +112,119 @@ function contentPatternToSearchResultHeader(pattern: ITextQuery | null, includes return removeNullFalseAndUndefined([ `# Query: ${escapeNewlines(pattern.contentPattern.pattern)}`, - (pattern.contentPattern.isCaseSensitive || pattern.contentPattern.isWordMatch || pattern.contentPattern.isRegExp) + (pattern.contentPattern.isCaseSensitive || pattern.contentPattern.isWordMatch || pattern.contentPattern.isRegExp || pattern.userDisabledExcludesAndIgnoreFiles) && `# Flags: ${coalesce([ pattern.contentPattern.isCaseSensitive && 'CaseSensitive', pattern.contentPattern.isWordMatch && 'WordMatch', - pattern.contentPattern.isRegExp && 'RegExp' + pattern.contentPattern.isRegExp && 'RegExp', + pattern.userDisabledExcludesAndIgnoreFiles && 'IgnoreExcludeSettings' ]).join(' ')}`, includes ? `# Including: ${includes}` : undefined, excludes ? `# Excluding: ${excludes}` : undefined, '' ]); -} +}; + +const searchHeaderToContentPattern = (header: string[]): { pattern: string, flags: { regex: boolean, wholeWord: boolean, caseSensitive: boolean, ignoreExcludes: boolean }, includes: string, excludes: string } => { + const query = { + pattern: '', + flags: { regex: false, caseSensitive: false, ignoreExcludes: false, wholeWord: false }, + includes: '', + excludes: '' + }; + + const unescapeNewlines = (str: string) => str.replace(/\\\\/g, '\\').replace(/\\n/g, '\n'); + const parseYML = /^# ([^:]*): (.*)$/; + for (const line of header) { + const parsed = parseYML.exec(line); + if (!parsed) { continue; } + const [, key, value] = parsed; + switch (key) { + case 'Query': query.pattern = unescapeNewlines(value); break; + case 'Including': query.includes = value; break; + case 'Excluding': query.excludes = value; break; + case 'Flags': { + query.flags = { + regex: value.indexOf('RegExp') !== -1, + caseSensitive: value.indexOf('CaseSensitive') !== -1, + ignoreExcludes: value.indexOf('IgnoreExcludeSettings') !== -1, + wholeWord: value.indexOf('WordMatch') !== -1 + }; + } + } + } + + return query; +}; const serializeSearchResultForEditor = (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, labelFormatter: (x: URI) => string): SearchResultSerialization => { const header = contentPatternToSearchResultHeader(searchResult.query, rawIncludePattern, rawExcludePattern); const allResults = flattenSearchResultSerializations( - flatten(searchResult.folderMatches() - .map(folderMatch => folderMatch.matches() + flatten(searchResult.folderMatches().sort(searchMatchComparer) + .map(folderMatch => folderMatch.matches().sort(searchMatchComparer) .map(fileMatch => fileMatchToSearchResultFormat(fileMatch, labelFormatter))))); return { matchRanges: allResults.matchRanges.map(translateRangeLines(header.length)), text: header.concat(allResults.text) }; }; +export const refreshActiveEditorSearch = + async (editorService: IEditorService, instantiationService: IInstantiationService, contextService: IWorkspaceContextService, labelService: ILabelService, configurationService: IConfigurationService) => { + const model = editorService.activeTextEditorWidget?.getModel(); + if (!model) { return; } + + const textModel = model as ITextModel; + + const header = textModel.getValueInRange(new Range(1, 1, 5, 1)) + .split(lineDelimiter) + .filter(line => line.indexOf('# ') === 0); + + const contentPattern = searchHeaderToContentPattern(header); + + const content: IPatternInfo = { + pattern: contentPattern.pattern, + isRegExp: contentPattern.flags.regex, + isCaseSensitive: contentPattern.flags.caseSensitive, + isWordMatch: contentPattern.flags.wholeWord + }; + + const options: ITextQueryBuilderOptions = { + _reason: 'searchEditor', + extraFileResources: instantiationService.invokeFunction(getOutOfWorkspaceEditorResources), + maxResults: 10000, + disregardIgnoreFiles: contentPattern.flags.ignoreExcludes, + disregardExcludeSettings: contentPattern.flags.ignoreExcludes, + excludePattern: contentPattern.excludes, + includePattern: contentPattern.includes, + previewOptions: { + matchLines: 1, + charsPerLine: 1000 + }, + isSmartCase: configurationService.getValue('search').smartCase, + expandPatterns: true + }; + + const folderResources = contextService.getWorkspace().folders; + + let query: ITextQuery; + try { + const queryBuilder = instantiationService.createInstance(QueryBuilder); + query = queryBuilder.text(content, folderResources.map(folder => folder.uri), options); + } catch (err) { + return; + } + + const searchModel = instantiationService.createInstance(SearchModel); + await searchModel.search(query); + + const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true }); + const results = serializeSearchResultForEditor(searchModel.searchResult, '', '', labelFormatter); + + textModel.setValue(results.text.join(lineDelimiter)); + textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'findMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); + }; + + export const createEditorFromSearchResult = async (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, labelService: ILabelService, editorService: IEditorService) => { const searchTerm = searchResult.query?.contentPattern.pattern.replace(/[^\w-_.]/g, '') || 'Search'; @@ -154,5 +251,4 @@ export const createEditorFromSearchResult = const model = control.getModel() as ITextModel; model.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'findMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); - }; diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index 2b5ff708245..589eeafc837 100644 --- a/src/vs/workbench/contrib/search/common/constants.ts +++ b/src/vs/workbench/contrib/search/common/constants.ts @@ -16,6 +16,7 @@ export const CopyPathCommandId = 'search.action.copyPath'; export const CopyMatchCommandId = 'search.action.copyMatch'; export const CopyAllCommandId = 'search.action.copyAll'; export const OpenInEditorCommandId = 'search.action.openInEditor'; +export const RerunEditorSearchCommandId = 'search.action.rerunEditorSearch'; export const ClearSearchHistoryCommandId = 'search.action.clearHistory'; export const FocusSearchListCommandID = 'search.action.focusSearchList'; export const ReplaceActionId = 'search.action.replace'; From b8d63e0fcf76f828c7ebb7939afbb58215df0f42 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 25 Nov 2019 04:08:14 +0000 Subject: [PATCH 15/58] Fix #83059 --- src/vs/workbench/contrib/search/browser/searchView.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index cb734ff0e65..b57e6f8d3bb 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -569,6 +569,7 @@ export class SearchView extends ViewletPane { progressComplete(); const messageEl = this.clearMessage(); dom.append(messageEl, $('p', undefined, afterReplaceAllMessage)); + this.reLayout(); }, (error) => { progressComplete(); errors.isPromiseCanceledError(error); From aa2d4f92f6116737847f68a5e394db0919863536 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 25 Nov 2019 10:23:30 +0100 Subject: [PATCH 16/58] Can only debug once with tasks 0.1.0 Part of #85343 --- src/vs/workbench/contrib/tasks/node/processTaskSystem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts index ba3adbd5a48..648fa8617f9 100644 --- a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts @@ -91,7 +91,7 @@ export class ProcessTaskSystem implements ITaskSystem { } public getBusyTasks(): Task[] { - return this.getActiveTasks(); + return []; } public run(task: Task): ITaskExecuteResult { From abbf52b1378e2976803f81b1317fe3a5e6e39c23 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 25 Nov 2019 10:35:26 +0100 Subject: [PATCH 17/58] remote inspect-port, workaround for #85490 --- .../extensions/node/extensionHostProcessSetup.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index 9955c682758..0f35c544319 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -27,6 +27,17 @@ interface ParsedExtHostArgs { uriTransformerPath?: string; } +// workaround for https://github.com/microsoft/vscode/issues/85490 +// remove --inspect-port=0 after start so that it doesn't trigger LSP debugging +(function removeInspectPort() { + for (let i = 0; i < process.execArgv.length; i++) { + if (process.execArgv[i] === '--inspect-port=0') { + process.execArgv.splice(i, 1); + i--; + } + } +})(); + const args = minimist(process.argv.slice(2), { string: [ 'uriTransformerPath' From a5d0ea3132be17674d5db3937dd0f30a2a576f71 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 25 Nov 2019 11:17:35 +0100 Subject: [PATCH 18/58] fix #84906 --- .../contrib/scm/browser/dirtydiffDecorator.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 30780a7a90f..a3cac4fbe01 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -1215,9 +1215,15 @@ class DirtyDiffItem { } } +interface IViewState { + readonly width: number; + readonly visibility: 'always' | 'hover'; +} + export class DirtyDiffWorkbenchController extends Disposable implements ext.IWorkbenchContribution, IModelRegistry { private enabled = false; + private viewState: IViewState = { width: 3, visibility: 'always' }; private models: ITextModel[] = []; private items: { [modelId: string]: DirtyDiffItem; } = Object.create(null); private readonly transientDisposables = this._register(new DisposableStore()); @@ -1262,15 +1268,20 @@ export class DirtyDiffWorkbenchController extends Disposable implements ext.IWor width = 3; } - this.stylesheet.innerHTML = `.monaco-editor .dirty-diff-modified,.monaco-editor .dirty-diff-added{border-left-width:${width}px;}`; + this.setViewState({ ...this.viewState, width }); } private onDidChangeDiffVisibiltiyConfiguration(): void { - const visibility = this.configurationService.getValue('scm.diffDecorationsGutterVisibility'); + const visibility = this.configurationService.getValue<'always' | 'hover'>('scm.diffDecorationsGutterVisibility'); + this.setViewState({ ...this.viewState, visibility }); + } + private setViewState(state: IViewState): void { + this.viewState = state; this.stylesheet.innerHTML = ` + .monaco-editor .dirty-diff-modified,.monaco-editor .dirty-diff-added{border-left-width:${state.width}px;} .monaco-editor .dirty-diff-modified, .monaco-editor .dirty-diff-added, .monaco-editor .dirty-diff-deleted { - opacity: ${visibility === 'always' ? 1 : 0}; + opacity: ${state.visibility === 'always' ? 1 : 0}; } `; } From 802a9eae622ce519a6b32fe738ae1efffad263a1 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 25 Nov 2019 11:23:02 +0100 Subject: [PATCH 19/58] fix dirty diff styles --- .../contrib/scm/browser/media/dirtydiffDecorator.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css b/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css index 1c7d01f9e35..472114eedd5 100644 --- a/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css +++ b/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css @@ -39,7 +39,7 @@ transition: height 80ms linear; } -.monaco-editor .margin-view-overlays > div:hover > .dirty-diff-glyph:before { +.monaco-editor .margin-view-overlays .dirty-diff-glyph:hover::before { position: absolute; content: ''; height: 100%; @@ -47,7 +47,7 @@ left: -6px; } -.monaco-editor .margin-view-overlays > div:hover > .dirty-diff-deleted:after { +.monaco-editor .margin-view-overlays .dirty-diff-deleted:hover::after { bottom: 0; border-top-width: 0; border-bottom-width: 0; @@ -56,4 +56,4 @@ /* Hide glyph decorations when inside the inline diff editor */ .monaco-editor.modified-in-monaco-diff-editor .margin-view-overlays > div > .dirty-diff-glyph { display: none; -} \ No newline at end of file +} From 6941792f287ea8e293750e51714beef86b6bfdb1 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 25 Nov 2019 11:43:23 +0100 Subject: [PATCH 20/58] fixes #85456 --- .../workbench/contrib/preferences/browser/settingsEditor2.ts | 4 ++-- src/vs/workbench/contrib/preferences/browser/settingsTree.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 3cdd39a86ed..abd4821d747 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -552,7 +552,7 @@ export class SettingsEditor2 extends BaseEditor { this.createFocusSink( bodyContainer, e => { - if (DOM.findParentWithClass(e.relatedTarget, 'monaco-list')) { + if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { if (this.settingsTree.scrollTop > 0) { const firstElement = this.settingsTree.firstVisibleElement; this.settingsTree.reveal(firstElement, 0.1); @@ -574,7 +574,7 @@ export class SettingsEditor2 extends BaseEditor { this.createFocusSink( bodyContainer, e => { - if (DOM.findParentWithClass(e.relatedTarget, 'monaco-list')) { + if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { if (this.settingsTree.scrollTop < this.settingsTree.scrollHeight) { const lastElement = this.settingsTree.lastVisibleElement; this.settingsTree.reveal(lastElement, 0.9); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 592b3194e54..e6fbc1582d8 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1516,6 +1516,8 @@ export class SettingsTree extends ObjectTree { } })); + this.getHTMLElement().classList.add('settings-editor-tree'); + this.disposables.add(attachStyler(themeService, { listBackground: editorBackground, listActiveSelectionBackground: editorBackground, From c2c643947bf18030c1af595e590cce6806aae0a5 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 25 Nov 2019 12:26:01 +0100 Subject: [PATCH 21/58] editor: fix word wrap settings in simple editor --- src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts index 29f6839f84b..a6b6117b2aa 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts @@ -211,6 +211,10 @@ class ToggleWordWrapController extends Disposable implements IEditorContribution // in the settings editor... return; } + if (this.editor.isSimpleWidget) { + // in a simple widget... + return; + } // Ensure correct word wrap settings const newModel = this.editor.getModel(); if (!newModel) { From 54f9bccc9922ae24e59dccb33c568a6efb2eaae2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 25 Nov 2019 07:22:37 -0800 Subject: [PATCH 22/58] Rename command --- .../terminal/browser/terminal.contribution.ts | 12 ++++---- .../terminal/browser/terminalActions.ts | 28 ++++++++----------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 23c0570df6a..55328d519b1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -20,7 +20,7 @@ import * as panel from 'vs/workbench/browser/panel'; import { getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { Extensions as QuickOpenExtensions, IQuickOpenRegistry, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; -import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand, CreateNewWithCwdTerminalCommand, RenameActiveTerminalCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand, CreateNewWithCwdTerminalCommand, RenameWithArgTerminalCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -612,19 +612,19 @@ if (BrowserFeatures.clipboard.readText) { } })).register(); -(new RenameActiveTerminalCommand({ - id: RenameActiveTerminalCommand.ID, +(new RenameWithArgTerminalCommand({ + id: RenameWithArgTerminalCommand.ID, precondition: undefined, description: { - description: RenameActiveTerminalCommand.LABEL, + description: RenameWithArgTerminalCommand.LABEL, args: [{ name: 'args', schema: { type: 'object', - required: ['newName'], + required: ['name'], properties: { newName: { - description: RenameActiveTerminalCommand.NEW_NAME_ARG_LABEL, + description: RenameWithArgTerminalCommand.NAME_ARG_LABEL, type: 'string', minLength: 1 } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index c083c93e567..fe82a9c3b6e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -1055,35 +1055,29 @@ export class RenameTerminalAction extends Action { }); } } -export class RenameActiveTerminalCommand extends Command { +export class RenameWithArgTerminalCommand extends Command { public static readonly ID = TERMINAL_COMMAND_ID.RENAME_NONINTERACTIVE; - public static readonly LABEL = nls.localize( - 'workbench.action.terminal.renameNoninteractive', - 'Rename the Currently Active Terminal' - ); - public static readonly NEW_NAME_ARG_LABEL = nls.localize( - 'workbench.action.terminal.renameNoninteractive.newName', - 'The new name for the terminal' - ); + public static readonly LABEL = nls.localize('workbench.action.terminal.renameWithArg', "Rename the Currently Active Terminal"); + public static readonly NAME_ARG_LABEL = nls.localize('workbench.action.terminal.renameWithArg.name', "The new name for the terminal"); public runCommand( accessor: ServicesAccessor, - args: { - newName: string; - } + args?: { name?: string } ): void { const notificationService = accessor.get(INotificationService); const terminalInstance = accessor.get(ITerminalService).getActiveInstance(); if (!terminalInstance) { - notificationService.warn(nls.localize( - RenameActiveTerminalCommand.LABEL + '.noActiveTerminal', - 'No active terminal to rename' - )); + notificationService.warn(nls.localize('workbench.action.terminal.renameWithArg.noTerminal', "No active terminal to rename")); return; } - terminalInstance.setTitle(args.newName, TitleEventSource.Api); + if (!args || !args.name) { + notificationService.warn(nls.localize('workbench.action.terminal.renameWithArg.noName', "No name argument provided")); + return; + } + + terminalInstance.setTitle(args.name, TitleEventSource.Api); } } From 1b22a6ce3d587fa4ab02e6b2c49608053e01ae20 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 25 Nov 2019 07:25:46 -0800 Subject: [PATCH 23/58] Rename with arg command id --- src/vs/workbench/contrib/terminal/browser/terminalActions.ts | 2 +- src/vs/workbench/contrib/terminal/common/terminal.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index fe82a9c3b6e..d0d206b55e2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -1056,7 +1056,7 @@ export class RenameTerminalAction extends Action { } } export class RenameWithArgTerminalCommand extends Command { - public static readonly ID = TERMINAL_COMMAND_ID.RENAME_NONINTERACTIVE; + public static readonly ID = TERMINAL_COMMAND_ID.RENAME_WITH_ARG; public static readonly LABEL = nls.localize('workbench.action.terminal.renameWithArg', "Rename the Currently Active Terminal"); public static readonly NAME_ARG_LABEL = nls.localize('workbench.action.terminal.renameWithArg.name', "The new name for the terminal"); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 03c390ebed6..2700e31e8ac 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -453,7 +453,7 @@ export const enum TERMINAL_COMMAND_ID { CLEAR_SELECTION = 'workbench.action.terminal.clearSelection', MANAGE_WORKSPACE_SHELL_PERMISSIONS = 'workbench.action.terminal.manageWorkspaceShellPermissions', RENAME = 'workbench.action.terminal.rename', - RENAME_NONINTERACTIVE = 'workbench.action.terminal.renameNoninteractive', + RENAME_WITH_ARG = 'workbench.action.terminal.renameWithArg', FIND_WIDGET_FOCUS = 'workbench.action.terminal.focusFindWidget', FIND_WIDGET_HIDE = 'workbench.action.terminal.hideFindWidget', QUICK_OPEN_TERM = 'workbench.action.quickOpenTerm', From d7def1653d712e8d991d86594b29dd1fb5806d82 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 25 Nov 2019 07:32:35 -0800 Subject: [PATCH 24/58] Correct auto-complete --- .../workbench/contrib/terminal/browser/terminal.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 55328d519b1..661e638a8e6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -623,7 +623,7 @@ if (BrowserFeatures.clipboard.readText) { type: 'object', required: ['name'], properties: { - newName: { + name: { description: RenameWithArgTerminalCommand.NAME_ARG_LABEL, type: 'string', minLength: 1 From 16d462571593113bdada3770bebae1b6d96ac841 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 25 Nov 2019 07:36:52 -0800 Subject: [PATCH 25/58] Include backtick in terminal word separators Fixes #84483 --- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 77e3a5e70b7..8668c77070a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -472,7 +472,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { fastScrollModifier: 'alt', fastScrollSensitivity: editorOptions.fastScrollSensitivity, scrollSensitivity: editorOptions.mouseWheelScrollSensitivity, - rendererType: config.rendererType === 'auto' || config.rendererType === 'experimentalWebgl' ? 'canvas' : config.rendererType + rendererType: config.rendererType === 'auto' || config.rendererType === 'experimentalWebgl' ? 'canvas' : config.rendererType, + wordSeparator: ' ()[]{}\',:;"`' }); this._xterm = xterm; this._xtermCore = (xterm as any)._core as XTermCore; From 9227460df47ab235de043fd2242f1be9bd166bf8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 25 Nov 2019 16:58:47 +0100 Subject: [PATCH 26/58] don't over-encode, do try to decode, fixes #85521 --- src/vs/editor/browser/services/openerService.ts | 15 ++++++++++----- .../workbench/api/common/extHostTypeConverters.ts | 9 ++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index ead9bd31bfb..a33a4ec776f 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -34,12 +34,17 @@ class CommandOpener implements IOpener { // execute as command let args: any = []; try { - args = parse(target.query); - if (!Array.isArray(args)) { - args = [args]; + args = parse(decodeURIComponent(target.query)); + } catch { + // ignore and retry + try { + args = parse(target.query); + } catch { + // ignore error } - } catch (e) { - // ignore error + } + if (!Array.isArray(args)) { + args = [args]; } await this._commandService.executeCommand(target.path, ...args); return true; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 08cfac73625..a1d98fee135 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -289,16 +289,23 @@ export namespace MarkdownString { if (!data) { return part; } + let changed = false; data = cloneAndChange(data, value => { if (URI.isUri(value)) { const key = `__uri_${Math.random().toString(16).slice(2, 8)}`; bucket[key] = value; + changed = true; return key; } else { return undefined; } }); - return encodeURIComponent(JSON.stringify(data)); + + if (!changed) { + return part; + } + + return JSON.stringify(data); } export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString { From b04c6f9e2edf62625a0098fa1adf80d96e5d6aa7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 25 Nov 2019 17:06:16 +0100 Subject: [PATCH 27/58] update API command for #85533 --- src/vs/workbench/api/common/extHostApiCommands.ts | 2 +- .../contrib/callHierarchy/browser/callHierarchy.ts | 2 +- .../electron-browser/api/extHostApiCommands.test.ts | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 67e9ba8669e..f671ebd74bd 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -86,7 +86,7 @@ const newCommands: ApiCommand[] = [ new ApiCommand( 'vscode.prepareCallHierarchy', '_executePrepareCallHierarchy', 'Prepare call hierarchy at a position inside a document', [ApiCommandArgument.Uri, ApiCommandArgument.Position], - new ApiCommandResult('A CallHierarchyItem or undefined', v => typeConverters.CallHierarchyItem.to(v)) + new ApiCommandResult('A CallHierarchyItem or undefined', v => v.map(typeConverters.CallHierarchyItem.to)) ), new ApiCommand( 'vscode.provideIncomingCalls', '_executeProvideIncomingCalls', 'Compute incoming calls for an item', diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts index cad37346668..ccfe8725195 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts @@ -172,7 +172,7 @@ CommandsRegistry.registerCommand('_executePrepareCallHierarchy', async (accessor _models.delete(key); } }); - return model.root; + return [model.root]; } finally { dispose(textModelReference); diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index 99d627ed559..f10a77afa63 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -885,16 +885,17 @@ suite('ExtHostLanguageFeatureCommands', function () { await rpcProtocol.sync(); - const root = await commands.executeCommand('vscode.prepareCallHierarchy', model.uri, new types.Position(0, 0)); + const root = await commands.executeCommand('vscode.prepareCallHierarchy', model.uri, new types.Position(0, 0)); - assert.ok(root); - assert.equal(root.name, 'ROOT'); + assert.ok(Array.isArray(root)); + assert.equal(root.length, 1); + assert.equal(root[0].name, 'ROOT'); - const incoming = await commands.executeCommand('vscode.provideIncomingCalls', root); + const incoming = await commands.executeCommand('vscode.provideIncomingCalls', root[0]); assert.equal(incoming.length, 1); assert.equal(incoming[0].from.name, 'INCOMING'); - const outgoing = await commands.executeCommand('vscode.provideOutgoingCalls', root); + const outgoing = await commands.executeCommand('vscode.provideOutgoingCalls', root[0]); assert.equal(outgoing.length, 1); assert.equal(outgoing[0].to.name, 'OUTGOING'); }); From 52839deb1c8a9c73a793f0463ea1dd21c1fa14b7 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 25 Nov 2019 09:03:26 -0800 Subject: [PATCH 28/58] Update icons and remove unused svgs --- .../browser/parts/editor/editorActions.ts | 2 +- .../files/browser/fileActions.contribution.ts | 16 +++------ .../files/browser/media/check-dark.svg | 3 -- .../files/browser/media/check-light.svg | 3 -- .../files/browser/media/collapse-all-dark.svg | 4 --- .../files/browser/media/collapse-all-hc.svg | 4 --- .../browser/media/collapse-all-light.svg | 4 --- .../files/browser/media/explorerviewlet.css | 13 ------- .../files/browser/media/fileactions.css | 35 ------------------- .../files/browser/media/preview-dark.svg | 4 --- .../files/browser/media/preview-light.svg | 4 --- .../media/split-editor-horizontal-dark.svg | 3 -- .../media/split-editor-horizontal-hc.svg | 3 -- .../media/split-editor-horizontal-light.svg | 7 ---- .../media/split-editor-vertical-dark.svg | 3 -- .../media/split-editor-vertical-hc.svg | 3 -- .../media/split-editor-vertical-light.svg | 7 ---- .../contrib/files/browser/media/undo-dark.svg | 3 -- .../files/browser/media/undo-light.svg | 3 -- 19 files changed, 5 insertions(+), 119 deletions(-) delete mode 100644 src/vs/workbench/contrib/files/browser/media/check-dark.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/check-light.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/collapse-all-dark.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/collapse-all-hc.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/collapse-all-light.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/preview-dark.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/preview-light.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-dark.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-hc.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-light.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-vertical-dark.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-vertical-hc.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-vertical-light.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/undo-dark.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/undo-light.svg diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 70b32efb074..b7e65d14579 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -418,7 +418,7 @@ export class OpenToSideFromQuickOpenAction extends Action { updateClass(): void { const preferredDirection = preferredSideBySideGroupDirection(this.configurationService); - this.class = (preferredDirection === GroupDirection.RIGHT) ? 'quick-open-sidebyside-vertical' : 'quick-open-sidebyside-horizontal'; + this.class = (preferredDirection === GroupDirection.RIGHT) ? 'codicon-split-horizontal' : 'codicon-split-vertical'; } run(context: any): Promise { diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index aabb8afd4af..8040b6b0bb9 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -21,14 +21,12 @@ import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOS import { AutoSaveAfterShortDelayContext } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; -import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { WorkspaceFolderCountContext, IsWebContext } from 'vs/workbench/browser/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { OpenFileFolderAction, OpenFileAction, OpenFolderAction, OpenWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ActiveEditorIsReadonlyContext, DirtyWorkingCopiesContext, ActiveEditorContext } from 'vs/workbench/common/editor'; import { SidebarFocusContext } from 'vs/workbench/common/viewlet'; -import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; // Contribute Global Actions const category = { value: nls.localize('filesCategory', "File"), original: 'File' }; @@ -181,23 +179,17 @@ export function appendEditorTitleContextMenuItem(id: string, title: string, when } // Editor Title Menu for Conflict Resolution -appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite file contents"), { - light: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/check-light.svg`)), - dark: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/check-dark.svg`)) -}, -10, acceptLocalChangesCommand); -appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to file contents"), { - light: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/undo-light.svg`)), - dark: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/undo-dark.svg`)) -}, -9, revertLocalChangesCommand); +appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite file contents"), 'codicon-check', -10, acceptLocalChangesCommand); +appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to file contents"), 'codicon-discard', -9, revertLocalChangesCommand); -function appendSaveConflictEditorTitleAction(id: string, title: string, iconLocation: { dark: URI; light?: URI; }, order: number, command: ICommandHandler): void { +function appendSaveConflictEditorTitleAction(id: string, title: string, iconClassName: string, order: number, command: ICommandHandler): void { // Command CommandsRegistry.registerCommand(id, command); // Action MenuRegistry.appendMenuItem(MenuId.EditorTitle, { - command: { id, title, iconLocation }, + command: { id, title, iconClassName }, when: ContextKeyExpr.equals(CONFLICT_RESOLUTION_CONTEXT, true), group: 'navigation', order diff --git a/src/vs/workbench/contrib/files/browser/media/check-dark.svg b/src/vs/workbench/contrib/files/browser/media/check-dark.svg deleted file mode 100644 index 51674695e1f..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/check-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/check-light.svg b/src/vs/workbench/contrib/files/browser/media/check-light.svg deleted file mode 100644 index 7b1da6d7208..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/check-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/collapse-all-dark.svg b/src/vs/workbench/contrib/files/browser/media/collapse-all-dark.svg deleted file mode 100644 index 4862c55dbeb..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/collapse-all-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/collapse-all-hc.svg b/src/vs/workbench/contrib/files/browser/media/collapse-all-hc.svg deleted file mode 100644 index 05f920b29b6..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/collapse-all-hc.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/collapse-all-light.svg b/src/vs/workbench/contrib/files/browser/media/collapse-all-light.svg deleted file mode 100644 index 6359b42e623..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/collapse-all-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css index 8933103c4d3..d20f338055d 100644 --- a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css @@ -169,16 +169,3 @@ .hc-black .monaco-workbench .explorer-viewlet .editor-group { line-height: 20px; } - -/* TODO @misolori convert these to use icon font, for the debug viewlet */ -.monaco-workbench .explorer-action.collapse-explorer { - background: url("collapse-all-light.svg") 50% no-repeat; -} - -.vs-dark .monaco-workbench .explorer-action.collapse-explorer { - background: url("collapse-all-dark.svg") 50% no-repeat; -} - -.hc-black .monaco-workbench .explorer-action.collapse-explorer { - background: url("collapse-all-hc.svg") 50% no-repeat; -} diff --git a/src/vs/workbench/contrib/files/browser/media/fileactions.css b/src/vs/workbench/contrib/files/browser/media/fileactions.css index 890cd46cef9..a6320e959bf 100644 --- a/src/vs/workbench/contrib/files/browser/media/fileactions.css +++ b/src/vs/workbench/contrib/files/browser/media/fileactions.css @@ -3,41 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* Split editor vertical */ -.monaco-workbench .quick-open-sidebyside-vertical { - background-image: url("split-editor-vertical-light.svg"); -} - -.vs-dark .monaco-workbench .quick-open-sidebyside-vertical { - background-image: url("split-editor-vertical-dark.svg"); -} - -.hc-black .monaco-workbench .quick-open-sidebyside-vertical { - background-image: url("split-editor-vertical-hc.svg"); -} - -/* Split editor horizontal */ -.monaco-workbench .quick-open-sidebyside-horizontal { - background-image: url("split-editor-horizontal-light.svg"); -} - -.vs-dark .monaco-workbench .quick-open-sidebyside-horizontal { - background-image: url("split-editor-horizontal-dark.svg"); -} - -.hc-black .monaco-workbench .quick-open-sidebyside-horizontal { - background-image: url("split-editor-horizontal-hc.svg"); -} - -.monaco-workbench .file-editor-action.action-open-preview { - background: url("preview-light.svg") center center no-repeat; -} - -.vs-dark .monaco-workbench .file-editor-action.action-open-preview, -.hc-black .monaco-workbench .file-editor-action.action-open-preview { - background: url("preview-dark.svg") center center no-repeat; -} - .explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row.dirty:not(:hover) > .monaco-action-bar .codicon-close::before { content: "\ea71"; } diff --git a/src/vs/workbench/contrib/files/browser/media/preview-dark.svg b/src/vs/workbench/contrib/files/browser/media/preview-dark.svg deleted file mode 100644 index 1d59877196b..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/preview-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/preview-light.svg b/src/vs/workbench/contrib/files/browser/media/preview-light.svg deleted file mode 100644 index 3f1152fc3cd..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/preview-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-dark.svg b/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-dark.svg deleted file mode 100644 index 419c21be4f6..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-hc.svg b/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-hc.svg deleted file mode 100644 index 7565fd3c168..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-light.svg b/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-light.svg deleted file mode 100644 index 405291c14dd..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-light.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-dark.svg b/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-dark.svg deleted file mode 100644 index 8c22a7c5bfe..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-hc.svg b/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-hc.svg deleted file mode 100644 index 82c19d0c8fc..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-light.svg b/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-light.svg deleted file mode 100644 index d5a2e9415b0..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-light.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/undo-dark.svg b/src/vs/workbench/contrib/files/browser/media/undo-dark.svg deleted file mode 100644 index de85d6ba679..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/undo-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/undo-light.svg b/src/vs/workbench/contrib/files/browser/media/undo-light.svg deleted file mode 100644 index b70626957d0..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/undo-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - From 5f27d2a9f9646e53edd5d816444b8d1e7c664862 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 25 Nov 2019 09:19:04 -0800 Subject: [PATCH 29/58] Replace checkbox icon with icon font --- src/vs/base/browser/ui/checkbox/check-dark.svg | 3 --- src/vs/base/browser/ui/checkbox/check-light.svg | 3 --- src/vs/base/browser/ui/checkbox/checkbox.css | 8 -------- src/vs/base/browser/ui/checkbox/checkbox.ts | 6 +++--- 4 files changed, 3 insertions(+), 17 deletions(-) delete mode 100644 src/vs/base/browser/ui/checkbox/check-dark.svg delete mode 100644 src/vs/base/browser/ui/checkbox/check-light.svg diff --git a/src/vs/base/browser/ui/checkbox/check-dark.svg b/src/vs/base/browser/ui/checkbox/check-dark.svg deleted file mode 100644 index 865cc83c347..00000000000 --- a/src/vs/base/browser/ui/checkbox/check-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/checkbox/check-light.svg b/src/vs/base/browser/ui/checkbox/check-light.svg deleted file mode 100644 index e1a546660ed..00000000000 --- a/src/vs/base/browser/ui/checkbox/check-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/checkbox/checkbox.css b/src/vs/base/browser/ui/checkbox/checkbox.css index fa9aa082d39..578c9f239d5 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.css +++ b/src/vs/base/browser/ui/checkbox/checkbox.css @@ -43,11 +43,3 @@ opacity: 1; background-size: 16px !important; } - -.monaco-custom-checkbox.monaco-simple-checkbox.checked { - background: url('check-light.svg') center center no-repeat; -} - -.monaco-custom-checkbox.monaco-simple-checkbox.checked { - background: url('check-dark.svg') center center no-repeat; -} diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 8eb398bf2db..15edf5fb794 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -99,7 +99,7 @@ export class Checkbox extends Widget { this.domNode = document.createElement('div'); this.domNode.title = this._opts.title; - this.domNode.className = 'monaco-custom-checkbox codicon ' + (this._opts.actionClassName || '') + ' ' + (this._checked ? 'checked' : 'unchecked'); + this.domNode.className = 'monaco-custom-checkbox codicon ' + (this._opts.actionClassName || '') + ' ' + (this._checked ? 'codicon-check' : 'unchecked'); this.domNode.tabIndex = 0; this.domNode.setAttribute('role', 'checkbox'); this.domNode.setAttribute('aria-checked', String(this._checked)); @@ -143,9 +143,9 @@ export class Checkbox extends Widget { this._checked = newIsChecked; this.domNode.setAttribute('aria-checked', String(this._checked)); if (this._checked) { - this.domNode.classList.add('checked'); + this.domNode.classList.add('codicon-check'); } else { - this.domNode.classList.remove('checked'); + this.domNode.classList.remove('codicon-check'); } this.applyStyles(); From a153ad5ef38651b606fe89023b6f6e9d5ccdcec4 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 25 Nov 2019 09:27:31 -0800 Subject: [PATCH 30/58] Remove dead code --- .../parts/activitybar/media/activitybarpart.css | 10 ---------- .../parts/activitybar/media/ellipsis-activity-bar.svg | 5 ----- .../parts/activitybar/media/settings-activity-bar.svg | 3 --- 3 files changed, 18 deletions(-) delete mode 100644 src/vs/workbench/browser/parts/activitybar/media/ellipsis-activity-bar.svg delete mode 100644 src/vs/workbench/browser/parts/activitybar/media/settings-activity-bar.svg diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css index 51871f7106e..09bb59d2980 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css @@ -38,16 +38,6 @@ outline: 0 !important; /* activity bar indicates focus custom */ } -.monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-label.toggle-more { - mask: url('ellipsis-activity-bar.svg') no-repeat 50% 50%; - -webkit-mask: url('ellipsis-activity-bar.svg') no-repeat 50% 50%; -} - -.monaco-workbench .activitybar .global-activity .monaco-action-bar .action-label.update-activity { - mask: url('settings-activity-bar.svg') no-repeat 50% 50%; - -webkit-mask: url('settings-activity-bar.svg') no-repeat 50% 50%; -} - .monaco-workbench .activitybar > .content > .composite-bar { margin-bottom: auto; } diff --git a/src/vs/workbench/browser/parts/activitybar/media/ellipsis-activity-bar.svg b/src/vs/workbench/browser/parts/activitybar/media/ellipsis-activity-bar.svg deleted file mode 100644 index 6729ca3c90d..00000000000 --- a/src/vs/workbench/browser/parts/activitybar/media/ellipsis-activity-bar.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/vs/workbench/browser/parts/activitybar/media/settings-activity-bar.svg b/src/vs/workbench/browser/parts/activitybar/media/settings-activity-bar.svg deleted file mode 100644 index 5d5fbb8ea5e..00000000000 --- a/src/vs/workbench/browser/parts/activitybar/media/settings-activity-bar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - From 2834fa1ca1ef21f4909cb27766d98fb93941cd6b Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 25 Nov 2019 10:04:26 -0800 Subject: [PATCH 31/58] Update list filtering to use icon font cc @joaomoreno --- src/vs/base/browser/ui/list/list.css | 57 +++++-------------- .../base/browser/ui/list/media/close-dark.svg | 3 - .../base/browser/ui/list/media/close-hc.svg | 3 - .../browser/ui/list/media/close-light.svg | 3 - .../browser/ui/list/media/filter-dark.svg | 3 - .../base/browser/ui/list/media/filter-hc.svg | 3 - .../browser/ui/list/media/filter-light.svg | 3 - .../browser/ui/list/media/no-filter-dark.svg | 3 - .../browser/ui/list/media/no-filter-hc.svg | 3 - .../browser/ui/list/media/no-filter-light.svg | 3 - src/vs/base/browser/ui/tree/abstractTree.ts | 4 +- 11 files changed, 17 insertions(+), 71 deletions(-) delete mode 100644 src/vs/base/browser/ui/list/media/close-dark.svg delete mode 100644 src/vs/base/browser/ui/list/media/close-hc.svg delete mode 100644 src/vs/base/browser/ui/list/media/close-light.svg delete mode 100644 src/vs/base/browser/ui/list/media/filter-dark.svg delete mode 100644 src/vs/base/browser/ui/list/media/filter-hc.svg delete mode 100644 src/vs/base/browser/ui/list/media/filter-light.svg delete mode 100644 src/vs/base/browser/ui/list/media/no-filter-dark.svg delete mode 100644 src/vs/base/browser/ui/list/media/no-filter-hc.svg delete mode 100644 src/vs/base/browser/ui/list/media/no-filter-light.svg diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 1bf13b4ce11..c4b75d53cde 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -113,55 +113,28 @@ } .monaco-list-type-filter > .controls > * { + border: none; box-sizing: border-box; + -webkit-appearance: none; + -moz-appearance: none; + background: none; width: 16px; height: 16px; - margin: 0 0 0 2px; flex-shrink: 0; + margin: 0; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; +} + +.monaco-list-type-filter > .controls > .filter:checked::before { + content: "\eb83" !important; /* codicon-list-filter */ } .monaco-list-type-filter > .controls > .filter { - -webkit-appearance: none; - -moz-appearance: none; - width: 16px; - height: 16px; - background: url("media/no-filter-light.svg"); - background-position: 50% 50%; - cursor: pointer; -} - -.monaco-list-type-filter > .controls > .filter:checked { - background-image: url("media/filter-light.svg"); -} - -.vs-dark .monaco-list-type-filter > .controls > .filter { - background-image: url("media/no-filter-dark.svg"); -} - -.vs-dark .monaco-list-type-filter > .controls > .filter:checked { - background-image: url("media/filter-dark.svg"); -} - -.hc-black .monaco-list-type-filter > .controls > .filter { - background-image: url("media/no-filter-hc.svg"); -} - -.hc-black .monaco-list-type-filter > .controls > .filter:checked { - background-image: url("media/filter-hc.svg"); -} - -.monaco-list-type-filter > .controls > .clear { - border: none; - background: url("media/close-light.svg"); - cursor: pointer; -} - -.vs-dark .monaco-list-type-filter > .controls > .clear { - background-image: url("media/close-dark.svg"); -} - -.hc-black .monaco-list-type-filter > .controls > .clear { - background-image: url("media/close-hc.svg"); + margin-left: 4px; } .monaco-list-type-filter-message { diff --git a/src/vs/base/browser/ui/list/media/close-dark.svg b/src/vs/base/browser/ui/list/media/close-dark.svg deleted file mode 100644 index 7305a8f099a..00000000000 --- a/src/vs/base/browser/ui/list/media/close-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/close-hc.svg b/src/vs/base/browser/ui/list/media/close-hc.svg deleted file mode 100644 index 7305a8f099a..00000000000 --- a/src/vs/base/browser/ui/list/media/close-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/close-light.svg b/src/vs/base/browser/ui/list/media/close-light.svg deleted file mode 100644 index ecddcd665b5..00000000000 --- a/src/vs/base/browser/ui/list/media/close-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/filter-dark.svg b/src/vs/base/browser/ui/list/media/filter-dark.svg deleted file mode 100644 index 46c35f4374d..00000000000 --- a/src/vs/base/browser/ui/list/media/filter-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/filter-hc.svg b/src/vs/base/browser/ui/list/media/filter-hc.svg deleted file mode 100644 index d7b6bdd3923..00000000000 --- a/src/vs/base/browser/ui/list/media/filter-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/filter-light.svg b/src/vs/base/browser/ui/list/media/filter-light.svg deleted file mode 100644 index 2550d80cb70..00000000000 --- a/src/vs/base/browser/ui/list/media/filter-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/no-filter-dark.svg b/src/vs/base/browser/ui/list/media/no-filter-dark.svg deleted file mode 100644 index 6fc07d81a55..00000000000 --- a/src/vs/base/browser/ui/list/media/no-filter-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/no-filter-hc.svg b/src/vs/base/browser/ui/list/media/no-filter-hc.svg deleted file mode 100644 index 6fc07d81a55..00000000000 --- a/src/vs/base/browser/ui/list/media/no-filter-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/no-filter-light.svg b/src/vs/base/browser/ui/list/media/no-filter-light.svg deleted file mode 100644 index 3608b15d29e..00000000000 --- a/src/vs/base/browser/ui/list/media/no-filter-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 52e88cc5c9b..af6d9965870 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -634,14 +634,14 @@ class TypeFilterController implements IDisposable { const controls = append(this.domNode, $('.controls')); this._filterOnType = !!tree.options.filterOnType; - this.filterOnTypeDomNode = append(controls, $('input.filter')); + this.filterOnTypeDomNode = append(controls, $('input.filter.codicon.codicon-list-selection')); this.filterOnTypeDomNode.type = 'checkbox'; this.filterOnTypeDomNode.checked = this._filterOnType; this.filterOnTypeDomNode.tabIndex = -1; this.updateFilterOnTypeTitle(); domEvent(this.filterOnTypeDomNode, 'input')(this.onDidChangeFilterOnType, this, this.disposables); - this.clearDomNode = append(controls, $('button.clear')); + this.clearDomNode = append(controls, $('button.clear.codicon.codicon-close')); this.clearDomNode.tabIndex = -1; this.clearDomNode.title = localize('clear', "Clear"); From 9dbb30ebf71ad5f0791cbacd50fc712170048881 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 25 Nov 2019 10:10:09 -0800 Subject: [PATCH 32/58] Update drag icon --- src/vs/workbench/contrib/debug/browser/debugToolBar.ts | 2 +- .../contrib/debug/browser/media/debugToolBar.css | 6 ++++-- src/vs/workbench/contrib/debug/browser/media/drag.svg | 8 -------- 3 files changed, 5 insertions(+), 11 deletions(-) delete mode 100644 src/vs/workbench/contrib/debug/browser/media/drag.svg diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 4d8c423dbce..71c102e3515 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -141,7 +141,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.$el = dom.$('div.debug-toolbar'); this.$el.style.top = `${layoutService.getTitleBarOffset()}px`; - this.dragArea = dom.append(this.$el, dom.$('div.drag-area')); + this.dragArea = dom.append(this.$el, dom.$('div.drag-area.codicon.codicon-gripper')); const actionBarContainer = dom.append(this.$el, dom.$('div.action-bar-container')); this.debugToolBarMenu = menuService.createMenu(MenuId.DebugToolBar, contextKeyService); diff --git a/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css b/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css index 4ac6feef480..56d48297c37 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css @@ -28,8 +28,10 @@ cursor: grab; height: 32px; width: 16px; - background: url('drag.svg') center center no-repeat; - background-size: 16px 16px; + opacity: 0.5; + display: flex; + align-items: center; + justify-content: center; } .monaco-workbench .debug-toolbar .drag-area.dragged { diff --git a/src/vs/workbench/contrib/debug/browser/media/drag.svg b/src/vs/workbench/contrib/debug/browser/media/drag.svg deleted file mode 100644 index b6b93f31fdf..00000000000 --- a/src/vs/workbench/contrib/debug/browser/media/drag.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - From 37eeb3755aa07fbe632dfdb314792b585d130933 Mon Sep 17 00:00:00 2001 From: Andrew Liu Date: Mon, 25 Nov 2019 11:30:04 -0800 Subject: [PATCH 33/58] quick fix (#85506) --- extensions/markdown-language-features/src/features/preview.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index 2fbf35d8b20..ad8edc1c2ad 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -490,6 +490,9 @@ export class DynamicMarkdownPreview extends Disposable { } private async onDidClickPreview(line: number): Promise { + // fix #82457, find currently opened but unfocused source tab + await vscode.commands.executeCommand('markdown.showSource'); + for (const visibleEditor of vscode.window.visibleTextEditors) { if (this.isPreviewOf(visibleEditor.document.uri)) { const editor = await vscode.window.showTextDocument(visibleEditor.document, visibleEditor.viewColumn); From 24e826acd56c608439e6ff2047f7bb2c77a81131 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 25 Nov 2019 11:55:15 -0800 Subject: [PATCH 34/58] Add extension build scripts --- .../search-result/extension.webpack.config.js | 20 +++++++++++++++++++ extensions/search-result/package.json | 3 +++ 2 files changed, 23 insertions(+) create mode 100644 extensions/search-result/extension.webpack.config.js diff --git a/extensions/search-result/extension.webpack.config.js b/extensions/search-result/extension.webpack.config.js new file mode 100644 index 00000000000..de88398eca0 --- /dev/null +++ b/extensions/search-result/extension.webpack.config.js @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withDefaults = require('../shared.webpack.config'); + +module.exports = withDefaults({ + context: __dirname, + resolve: { + mainFields: ['module', 'main'] + }, + entry: { + extension: './src/extension.ts', + } +}); diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json index a625733aa8a..3358d81f093 100644 --- a/extensions/search-result/package.json +++ b/extensions/search-result/package.json @@ -15,6 +15,9 @@ "activationEvents": [ "*" ], + "scripts": { + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:search-result ./tsconfig.json" + }, "contributes": { "commands": [ { From 5c3aaed985774a1b21dbf2bdc922f52f4833759f Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 25 Nov 2019 13:58:40 -0800 Subject: [PATCH 35/58] Revert "Replace checkbox icon with icon font" This reverts commit 5f27d2a9f9646e53edd5d816444b8d1e7c664862. --- src/vs/base/browser/ui/checkbox/check-dark.svg | 3 +++ src/vs/base/browser/ui/checkbox/check-light.svg | 3 +++ src/vs/base/browser/ui/checkbox/checkbox.css | 8 ++++++++ src/vs/base/browser/ui/checkbox/checkbox.ts | 6 +++--- 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 src/vs/base/browser/ui/checkbox/check-dark.svg create mode 100644 src/vs/base/browser/ui/checkbox/check-light.svg diff --git a/src/vs/base/browser/ui/checkbox/check-dark.svg b/src/vs/base/browser/ui/checkbox/check-dark.svg new file mode 100644 index 00000000000..865cc83c347 --- /dev/null +++ b/src/vs/base/browser/ui/checkbox/check-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/checkbox/check-light.svg b/src/vs/base/browser/ui/checkbox/check-light.svg new file mode 100644 index 00000000000..e1a546660ed --- /dev/null +++ b/src/vs/base/browser/ui/checkbox/check-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/checkbox/checkbox.css b/src/vs/base/browser/ui/checkbox/checkbox.css index 578c9f239d5..fa9aa082d39 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.css +++ b/src/vs/base/browser/ui/checkbox/checkbox.css @@ -43,3 +43,11 @@ opacity: 1; background-size: 16px !important; } + +.monaco-custom-checkbox.monaco-simple-checkbox.checked { + background: url('check-light.svg') center center no-repeat; +} + +.monaco-custom-checkbox.monaco-simple-checkbox.checked { + background: url('check-dark.svg') center center no-repeat; +} diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 15edf5fb794..8eb398bf2db 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -99,7 +99,7 @@ export class Checkbox extends Widget { this.domNode = document.createElement('div'); this.domNode.title = this._opts.title; - this.domNode.className = 'monaco-custom-checkbox codicon ' + (this._opts.actionClassName || '') + ' ' + (this._checked ? 'codicon-check' : 'unchecked'); + this.domNode.className = 'monaco-custom-checkbox codicon ' + (this._opts.actionClassName || '') + ' ' + (this._checked ? 'checked' : 'unchecked'); this.domNode.tabIndex = 0; this.domNode.setAttribute('role', 'checkbox'); this.domNode.setAttribute('aria-checked', String(this._checked)); @@ -143,9 +143,9 @@ export class Checkbox extends Widget { this._checked = newIsChecked; this.domNode.setAttribute('aria-checked', String(this._checked)); if (this._checked) { - this.domNode.classList.add('codicon-check'); + this.domNode.classList.add('checked'); } else { - this.domNode.classList.remove('codicon-check'); + this.domNode.classList.remove('checked'); } this.applyStyles(); From 8062282fc769f8512b15b8a36df0168f19dc3016 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 25 Nov 2019 14:59:40 -0800 Subject: [PATCH 36/58] Fix build --- extensions/search-result/package.json | 2 +- extensions/search-result/syntaxes/searchResult.tmLanguage.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json index 3358d81f093..bc7c6cf951c 100644 --- a/extensions/search-result/package.json +++ b/extensions/search-result/package.json @@ -11,7 +11,7 @@ "categories": [ "Programming Languages" ], - "main": "out/extension.js", + "main": "./out/extension.js", "activationEvents": [ "*" ], diff --git a/extensions/search-result/syntaxes/searchResult.tmLanguage.json b/extensions/search-result/syntaxes/searchResult.tmLanguage.json index 80e9edfaa81..4de2a40ba40 100644 --- a/extensions/search-result/syntaxes/searchResult.tmLanguage.json +++ b/extensions/search-result/syntaxes/searchResult.tmLanguage.json @@ -3,7 +3,7 @@ "scopeName": "text.searchResult", "patterns": [ { - "match": "^# (Query|Flags|Include|Exclude): .*$", + "match": "^# (Query|Flags|Including|Excluding): .*$", "name": "comment" }, { From d1fab46c13b51074d584531c7c1d63d9d8466f99 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 25 Nov 2019 15:10:05 -0800 Subject: [PATCH 37/58] Remove "Settings editor preview" tip from settings GUI color token descriptions --- .../preferences/browser/settingsWidgets.ts | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index eb1bbf865cd..cc60be57781 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -23,33 +23,33 @@ import { disposableTimeout } from 'vs/base/common/async'; import { isUndefinedOrNull } from 'vs/base/common/types'; const $ = DOM.$; -export const settingsHeaderForeground = registerColor('settings.headerForeground', { light: '#444444', dark: '#e7e7e7', hc: '#ffffff' }, localize('headerForeground', "(For settings editor preview) The foreground color for a section header or active title.")); +export const settingsHeaderForeground = registerColor('settings.headerForeground', { light: '#444444', dark: '#e7e7e7', hc: '#ffffff' }, localize('headerForeground', "The foreground color for a section header or active title.")); export const modifiedItemIndicator = registerColor('settings.modifiedItemIndicator', { light: new Color(new RGBA(102, 175, 224)), dark: new Color(new RGBA(12, 125, 157)), hc: new Color(new RGBA(0, 73, 122)) -}, localize('modifiedItemForeground', "(For settings editor preview) The color of the modified setting indicator.")); +}, localize('modifiedItemForeground', "The color of the modified setting indicator.")); // Enum control colors -export const settingsSelectBackground = registerColor('settings.dropdownBackground', { dark: selectBackground, light: selectBackground, hc: selectBackground }, localize('settingsDropdownBackground', "(For settings editor preview) Settings editor dropdown background.")); -export const settingsSelectForeground = registerColor('settings.dropdownForeground', { dark: selectForeground, light: selectForeground, hc: selectForeground }, localize('settingsDropdownForeground', "(For settings editor preview) Settings editor dropdown foreground.")); -export const settingsSelectBorder = registerColor('settings.dropdownBorder', { dark: selectBorder, light: selectBorder, hc: selectBorder }, localize('settingsDropdownBorder', "(For settings editor preview) Settings editor dropdown border.")); -export const settingsSelectListBorder = registerColor('settings.dropdownListBorder', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, localize('settingsDropdownListBorder', "(For settings editor preview) Settings editor dropdown list border. This surrounds the options and separates the options from the description.")); +export const settingsSelectBackground = registerColor('settings.dropdownBackground', { dark: selectBackground, light: selectBackground, hc: selectBackground }, localize('settingsDropdownBackground', "Settings editor dropdown background.")); +export const settingsSelectForeground = registerColor('settings.dropdownForeground', { dark: selectForeground, light: selectForeground, hc: selectForeground }, localize('settingsDropdownForeground', "Settings editor dropdown foreground.")); +export const settingsSelectBorder = registerColor('settings.dropdownBorder', { dark: selectBorder, light: selectBorder, hc: selectBorder }, localize('settingsDropdownBorder', "Settings editor dropdown border.")); +export const settingsSelectListBorder = registerColor('settings.dropdownListBorder', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, localize('settingsDropdownListBorder', "Settings editor dropdown list border. This surrounds the options and separates the options from the description.")); // Bool control colors -export const settingsCheckboxBackground = registerColor('settings.checkboxBackground', { dark: simpleCheckboxBackground, light: simpleCheckboxBackground, hc: simpleCheckboxBackground }, localize('settingsCheckboxBackground', "(For settings editor preview) Settings editor checkbox background.")); -export const settingsCheckboxForeground = registerColor('settings.checkboxForeground', { dark: simpleCheckboxForeground, light: simpleCheckboxForeground, hc: simpleCheckboxForeground }, localize('settingsCheckboxForeground', "(For settings editor preview) Settings editor checkbox foreground.")); -export const settingsCheckboxBorder = registerColor('settings.checkboxBorder', { dark: simpleCheckboxBorder, light: simpleCheckboxBorder, hc: simpleCheckboxBorder }, localize('settingsCheckboxBorder', "(For settings editor preview) Settings editor checkbox border.")); +export const settingsCheckboxBackground = registerColor('settings.checkboxBackground', { dark: simpleCheckboxBackground, light: simpleCheckboxBackground, hc: simpleCheckboxBackground }, localize('settingsCheckboxBackground', "Settings editor checkbox background.")); +export const settingsCheckboxForeground = registerColor('settings.checkboxForeground', { dark: simpleCheckboxForeground, light: simpleCheckboxForeground, hc: simpleCheckboxForeground }, localize('settingsCheckboxForeground', "Settings editor checkbox foreground.")); +export const settingsCheckboxBorder = registerColor('settings.checkboxBorder', { dark: simpleCheckboxBorder, light: simpleCheckboxBorder, hc: simpleCheckboxBorder }, localize('settingsCheckboxBorder', "Settings editor checkbox border.")); // Text control colors -export const settingsTextInputBackground = registerColor('settings.textInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('textInputBoxBackground', "(For settings editor preview) Settings editor text input box background.")); -export const settingsTextInputForeground = registerColor('settings.textInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('textInputBoxForeground', "(For settings editor preview) Settings editor text input box foreground.")); -export const settingsTextInputBorder = registerColor('settings.textInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('textInputBoxBorder', "(For settings editor preview) Settings editor text input box border.")); +export const settingsTextInputBackground = registerColor('settings.textInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('textInputBoxBackground', "Settings editor text input box background.")); +export const settingsTextInputForeground = registerColor('settings.textInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('textInputBoxForeground', "Settings editor text input box foreground.")); +export const settingsTextInputBorder = registerColor('settings.textInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('textInputBoxBorder', "Settings editor text input box border.")); // Number control colors -export const settingsNumberInputBackground = registerColor('settings.numberInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('numberInputBoxBackground', "(For settings editor preview) Settings editor number input box background.")); -export const settingsNumberInputForeground = registerColor('settings.numberInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('numberInputBoxForeground', "(For settings editor preview) Settings editor number input box foreground.")); -export const settingsNumberInputBorder = registerColor('settings.numberInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('numberInputBoxBorder', "(For settings editor preview) Settings editor number input box border.")); +export const settingsNumberInputBackground = registerColor('settings.numberInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('numberInputBoxBackground', "Settings editor number input box background.")); +export const settingsNumberInputForeground = registerColor('settings.numberInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('numberInputBoxForeground', "Settings editor number input box foreground.")); +export const settingsNumberInputBorder = registerColor('settings.numberInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('numberInputBoxBorder', "Settings editor number input box border.")); registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const checkboxBackgroundColor = theme.getColor(settingsCheckboxBackground); From ffe3749d5afb046f289d04f915d8d4ade47c028e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 22 Nov 2019 16:36:01 -0800 Subject: [PATCH 38/58] Working on hooking up custom editor saveAs --- src/vs/vscode.proposed.d.ts | 7 ++++ .../api/browser/mainThreadWebview.ts | 1 + .../workbench/api/common/extHost.protocol.ts | 4 ++ src/vs/workbench/api/common/extHostWebview.ts | 10 +++++ .../customEditor/browser/customEditorInput.ts | 41 +++++++++++++++++-- .../customEditor/common/customEditor.ts | 15 ++++++- .../customEditor/common/customEditorModel.ts | 33 +++++++++++++-- 7 files changed, 104 insertions(+), 7 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6dddd463989..d96918ef4f5 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1250,6 +1250,13 @@ declare module 'vscode' { */ save(): Thenable; + /** + * + * @param resource Resource being saved. + * @param targetResource Location to save to. + */ + saveAs(resource: Uri, targetResource: Uri): Thenable; + /** * Event triggered by extensions to signal to VS Code that an edit has occurred. * diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 14c9efb3e55..a27f62c02d5 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -276,6 +276,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma model.onUndo(edits => { this._proxy.$undoEdits(handle, edits); }); model.onRedo(edits => { this._proxy.$redoEdits(handle, edits); }); model.onWillSave(e => { e.waitUntil(this._proxy.$onSave(handle)); }); + model.onWillSaveAs(e => { e.waitUntil(this._proxy.$onSaveAs(handle, e.resource.toJSON(), e.targetResource.toJSON())); }); webviewInput.onDispose(() => { this._customEditorService.models.disposeModel(model); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 5a735828101..72d18a284d4 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -590,11 +590,15 @@ export interface ExtHostWebviewsShape { $onMissingCsp(handle: WebviewPanelHandle, extensionId: string): void; $onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void; $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; + $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; + $undoEdits(handle: WebviewPanelHandle, edits: readonly any[]): void; $redoEdits(handle: WebviewPanelHandle, edits: readonly any[]): void; + $onSave(handle: WebviewPanelHandle): Promise; + $onSaveAs(handle: WebviewPanelHandle, resource: UriComponents, targetResource: UriComponents): Promise; } export interface MainThreadUrlsShape extends IDisposable { diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 3cf879271d3..d1081d0f11e 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -260,6 +260,11 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa await assertIsDefined(this._capabilities).editingCapability?.save(); } + + async _onSaveAs(resource: vscode.Uri, targetResource: vscode.Uri): Promise { + await assertIsDefined(this._capabilities).editingCapability?.saveAs(resource, targetResource); + } + private assertNotDisposed() { if (this._isDisposed) { throw new Error('Webview is disposed'); @@ -462,6 +467,11 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { return panel?._onSave(); } + async $onSaveAs(handle: WebviewPanelHandle, resource: UriComponents, targetResource: UriComponents): Promise { + const panel = this.getWebviewPanel(handle); + return panel?._onSaveAs(URI.revive(resource), URI.revive(targetResource)); + } + private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined { return this._webviewPanels.get(handle); } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 9d3653b9d8f..397cae24e28 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -9,6 +9,7 @@ import { UnownedDisposable } from 'vs/base/common/lifecycle'; import { basename } from 'vs/base/common/path'; import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; +import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; @@ -16,6 +17,7 @@ import { GroupIdentifier, IEditorInput, IRevertOptions, ISaveOptions, Verbosity import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { @@ -33,6 +35,8 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { @IWebviewWorkbenchService webviewWorkbenchService: IWebviewWorkbenchService, @ILabelService private readonly labelService: ILabelService, @ICustomEditorService private readonly customEditorService: ICustomEditorService, + @IEditorService private readonly editorService: IEditorService, + @IFileDialogService private readonly fileDialogService: IFileDialogService, ) { super(id, viewType, '', webview, webviewWorkbenchService, lifecycleService); this._editorResource = resource; @@ -101,9 +105,32 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { return this._model ? this._model.save(options) : Promise.resolve(false); } - public saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise { - // TODO@matt implement properly (see TextEditorInput#saveAs()) - return this._model ? this._model.save(options) : Promise.resolve(false); + public async saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise { + if (!this._model) { + return false; + } + + // Preserve view state by opening the editor first. In addition + // this allows the user to review the contents of the editor. + // let viewState: IEditorViewState | undefined = undefined; + // const editor = await this.editorService.openEditor(this, undefined, group); + // if (isTextEditor(editor)) { + // viewState = editor.getViewState(); + // } + + let dialogPath = this._editorResource; + // if (this._editorResource.scheme === Schemas.untitled) { + // dialogPath = this.suggestFileName(resource); + // } + + const target = await this.promptForPath(this._editorResource, dialogPath, options?.availableFileSystems); + if (!target) { + return false; // save cancelled + } + + await this._model.saveAs(this._editorResource, target, options); + + return true; } public revert(options?: IRevertOptions): Promise { @@ -115,4 +142,12 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { this._register(this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire())); return await super.resolve(); } + + protected async promptForPath(resource: URI, defaultUri: URI, availableFileSystems?: readonly string[]): Promise { + + // Help user to find a name for the file by opening it first + await this.editorService.openEditor({ resource, options: { revealIfOpened: true, preserveFocus: true } }); + + return this.fileDialogService.pickFileToSave({});//this.getSaveDialogOptions(defaultUri, availableFileSystems)); + } } diff --git a/src/vs/workbench/contrib/customEditor/common/customEditor.ts b/src/vs/workbench/contrib/customEditor/common/customEditor.ts index 09b882531fb..2e08c304d7a 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditor.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditor.ts @@ -48,16 +48,29 @@ export interface ICustomEditorModelManager { disposeModel(model: ICustomEditorModel): void; } +export interface CustomEditorSaveEvent { + readonly resource: URI; + readonly waitUntil: (until: Promise) => void; +} + +export interface CustomEditorSaveAsEvent { + readonly resource: URI; + readonly targetResource: URI; + readonly waitUntil: (until: Promise) => void; +} + export interface ICustomEditorModel extends IWorkingCopy { readonly onUndo: Event; readonly onRedo: Event; - readonly onWillSave: Event<{ waitUntil: (until: Promise) => void }>; + readonly onWillSave: Event; + readonly onWillSaveAs: Event; undo(): void; redo(): void; revert(options?: IRevertOptions): Promise; save(options?: ISaveOptions): Promise; + saveAs(resource: URI, targetResource: URI, currentOptions?: ISaveOptions): Promise; makeEdit(data: string): void; } diff --git a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts index ee49e9aaa14..2b36d9569d2 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts @@ -6,7 +6,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { ICustomEditorModel, CustomEditorEdit } from 'vs/workbench/contrib/customEditor/common/customEditor'; +import { ICustomEditorModel, CustomEditorEdit, CustomEditorSaveAsEvent, CustomEditorSaveEvent } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor'; @@ -47,9 +47,12 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel protected readonly _onRedo = this._register(new Emitter()); readonly onRedo = this._onRedo.event; - protected readonly _onWillSave = this._register(new Emitter<{ waitUntil: (until: Promise) => void }>()); + protected readonly _onWillSave = this._register(new Emitter()); readonly onWillSave = this._onWillSave.event; + protected readonly _onWillSaveAs = this._register(new Emitter()); + readonly onWillSaveAs = this._onWillSaveAs.event; + public makeEdit(data: string): void { this._edits.splice(this._currentEditIndex + 1, this._edits.length - this._currentEditIndex, data); this._currentEditIndex = this._edits.length - 1; @@ -62,7 +65,10 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel public async save(_options?: ISaveOptions): Promise { const untils: Promise[] = []; - const handler = { waitUntil: (until: Promise) => untils.push(until) }; + const handler: CustomEditorSaveEvent = { + resource: this._resource, + waitUntil: (until: Promise) => untils.push(until) + }; try { this._onWillSave.fire(handler); @@ -77,6 +83,27 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel return true; } + public async saveAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise { + const untils: Promise[] = []; + const handler: CustomEditorSaveAsEvent = { + resource, + targetResource, + waitUntil: (until: Promise) => untils.push(until) + }; + + try { + this._onWillSaveAs.fire(handler); + await Promise.all(untils); + } catch { + return false; + } + + this._savePoint = this._currentEditIndex; + this.updateDirty(); + + return true; + } + public async revert(_options?: IRevertOptions) { if (this._currentEditIndex === this._savePoint) { return true; From 872c3afdf0c262330af75ab30db7c2d13c8af18e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 22 Nov 2019 17:25:25 -0800 Subject: [PATCH 39/58] Update TS insiders version --- extensions/package.json | 2 +- extensions/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index 8d6065560c0..d1030bbe6dd 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.7.3-insiders.20191118" + "typescript": "3.7.3-insiders.20191123" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index f1a8a66e077..f2e7ae95079 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.7.3-insiders.20191118: - version "3.7.3-insiders.20191118" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3-insiders.20191118.tgz#86d3b1de1859d70b0a081b708a223f8f3c6a4d46" - integrity sha512-r06UsJwJzLwgRkf3Or2T5tBnUmp8RD5gOHkC6ax9mLHu5r7voo1WAUpvG09R92GvdEQsgZXxOUhdEJWEoc/5sA== +typescript@3.7.3-insiders.20191123: + version "3.7.3-insiders.20191123" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3-insiders.20191123.tgz#f3bef33a2a3f6e02f11bcc0c20b6f0de526f17fd" + integrity sha512-b+tLx4D0a6SeuaCa7iehdgkRKHsS67FkioQWw+0REjVNOYZ+AqJ0NjlnomK1hEUvSzSNrH9Du+m+Yiv7JlVpSg== From b52f1c7dae8a70d1d877e65b16b9496118690196 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Sat, 23 Nov 2019 09:44:02 -0800 Subject: [PATCH 40/58] Trying to hook up background to implement custom editor rename --- src/vs/base/common/lifecycle.ts | 37 ------------------- .../api/browser/mainThreadWebview.ts | 4 +- src/vs/workbench/common/editor.ts | 5 +++ .../customEditor/browser/customEditorInput.ts | 17 +++++++-- .../browser/customEditorInputFactory.ts | 3 +- .../customEditor/browser/customEditors.ts | 4 +- .../browser/editors/fileEditorTracker.ts | 26 +++++++++---- .../browser/dynamicWebviewEditorOverlay.ts | 2 +- .../webview/browser/webviewEditorInput.ts | 29 ++++++++++++--- .../browser/webviewWorkbenchService.ts | 8 ++-- 10 files changed, 72 insertions(+), 63 deletions(-) diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 8d1cbd5b996..651c5fa22a4 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -206,43 +206,6 @@ export class MutableDisposable implements IDisposable { } } -/** - * Wrapper class that stores a disposable that is not currently "owned" by anyone. - * - * Example use cases: - * - * - Express that a function/method will take ownership of a disposable parameter. - * - Express that a function returns a disposable that the caller must explicitly take ownership of. - */ -export class UnownedDisposable extends Disposable { - private _hasBeenAcquired = false; - private _value?: T; - - public constructor(value: T) { - super(); - this._value = value; - } - - public acquire(): T { - if (this._hasBeenAcquired) { - throw new Error('This disposable has already been acquired'); - } - this._hasBeenAcquired = true; - const value = this._value!; - this._value = undefined; - return value; - } - - public dispose() { - super.dispose(); - if (!this._hasBeenAcquired) { - this._hasBeenAcquired = true; - this._value!.dispose(); - this._value = undefined; - } - } -} - export interface IReference extends IDisposable { readonly object: T; } diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index a27f62c02d5..1ba9ed80383 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -278,7 +278,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma model.onWillSave(e => { e.waitUntil(this._proxy.$onSave(handle)); }); model.onWillSaveAs(e => { e.waitUntil(this._proxy.$onSaveAs(handle, e.resource.toJSON(), e.targetResource.toJSON())); }); - webviewInput.onDispose(() => { + webviewInput.onDisposeWebview(() => { this._customEditorService.models.disposeModel(model); }); @@ -326,7 +326,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) { input.webview.onDidClickLink((uri: URI) => this.onDidClickLink(handle, uri)); input.webview.onMessage((message: any) => this._proxy.$onMessage(handle, message)); - input.onDispose(() => { + input.onDisposeWebview(() => { this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { this._webviewInputs.delete(handle); }); diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index c2f430cfefe..d3d6a4e6693 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -398,6 +398,11 @@ export interface IEditorInput extends IDisposable { */ saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise; + /** + * Handles when the input is replaced, such as by renaming its backing resource. + */ + handleMove?(groupId: GroupIdentifier, uri: URI, options?: ITextEditorOptions): IEditorInput | undefined; + /** * Reverts this input. */ diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 397cae24e28..a6d5a037799 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -5,12 +5,12 @@ import { memoize } from 'vs/base/common/decorators'; import { Lazy } from 'vs/base/common/lazy'; -import { UnownedDisposable } from 'vs/base/common/lifecycle'; import { basename } from 'vs/base/common/path'; import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IEditorModel } from 'vs/platform/editor/common/editor'; +import { IEditorModel, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { GroupIdentifier, IEditorInput, IRevertOptions, ISaveOptions, Verbosity } from 'vs/workbench/common/editor'; @@ -18,6 +18,7 @@ import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/c import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { assertIsDefined } from 'vs/base/common/types'; export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { @@ -30,9 +31,10 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { resource: URI, viewType: string, id: string, - webview: Lazy>, + webview: Lazy, @ILifecycleService lifecycleService: ILifecycleService, @IWebviewWorkbenchService webviewWorkbenchService: IWebviewWorkbenchService, + @IInstantiationService private readonly instantiationService: IInstantiationService, @ILabelService private readonly labelService: ILabelService, @ICustomEditorService private readonly customEditorService: ICustomEditorService, @IEditorService private readonly editorService: IEditorService, @@ -150,4 +152,13 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { return this.fileDialogService.pickFileToSave({});//this.getSaveDialogOptions(defaultUri, availableFileSystems)); } + + public handleMove(groupId: GroupIdentifier, uri: URI, options?: ITextEditorOptions): IEditorInput | undefined { + const webview = assertIsDefined(this.takeOwnershipOfWebview()); + return this.instantiationService.createInstance(CustomFileEditorInput, + uri, + this.viewType, + this.id, + new Lazy(() => webview)); + } } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts index 8739a05780a..866f96ef5f5 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { UnownedDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -48,7 +47,7 @@ export class CustomEditorInputFactory extends WebviewEditorInputFactory { location: data.extensionLocation, id: data.extensionId } : undefined, data.group); - return new UnownedDisposable(webviewInput.webview); + return webviewInput.webview; }); const customInput = this._instantiationService.createInstance(CustomFileEditorInput, URI.from((data as any).editorResource), data.viewType, id, webview); diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 814c4d6b301..9e87a796443 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -6,7 +6,7 @@ import { coalesce, distinct, find, mergeSort } from 'vs/base/common/arrays'; import * as glob from 'vs/base/common/glob'; import { Lazy } from 'vs/base/common/lazy'; -import { Disposable, UnownedDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { basename, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -201,7 +201,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ ): CustomFileEditorInput { const id = generateUuid(); const webview = new Lazy(() => { - return new UnownedDisposable(this.webviewService.createWebviewEditorOverlay(id, { customClasses: options?.customClasses }, {})); + return this.webviewService.createWebviewEditorOverlay(id, { customClasses: options?.customClasses }, {}); }); const input = this.instantiationService.createInstance(CustomFileEditorInput, resource, viewType, id, webview); if (group) { diff --git a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts index ca9dba559e2..bcc39a73b37 100644 --- a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts @@ -27,7 +27,7 @@ import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor import { ResourceQueue, timeout } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { EditorActivation } from 'vs/platform/editor/common/editor'; +import { EditorActivation, ITextEditorOptions } from 'vs/platform/editor/common/editor'; export class FileEditorTracker extends Disposable implements IWorkbenchContribution { @@ -215,8 +215,8 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut private handleMovedFileInOpenedEditors(oldResource: URI, newResource: URI): void { this.editorGroupService.groups.forEach(group => { group.editors.forEach(editor => { - if (editor instanceof FileEditorInput) { - const resource = editor.getResource(); + const resource = editor.getResource(); + if (resource && (editor instanceof FileEditorInput || editor.handleMove)) { // Update Editor if file (or any parent of the input) got renamed or moved if (resources.isEqualOrParent(resource, oldResource)) { @@ -228,15 +228,27 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut reopenFileResource = resources.joinPath(newResource, resource.path.substr(index + oldResource.path.length + 1)); // parent folder got moved } + const options: ITextEditorOptions = { + preserveFocus: true, + pinned: group.isPinned(editor), + index: group.getIndexOfEditor(editor), + inactive: !group.isActive(editor), + }; + + if (editor.handleMove) { + const replacement = editor.handleMove(group.id, reopenFileResource, options); + if (replacement) { + this.editorService.replaceEditors([{ editor, replacement }], group); + return; + } + } + this.editorService.replaceEditors([{ editor: { resource }, replacement: { resource: reopenFileResource, options: { - preserveFocus: true, - pinned: group.isPinned(editor), - index: group.getIndexOfEditor(editor), - inactive: !group.isActive(editor), + ...options, viewState: this.getViewStateFor(oldResource, group) } }, diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index 945e6be9f4b..16b5b7e7d1d 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -99,7 +99,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewEd if (this._options.tryRestoreScrollPosition) { webview.initialScrollProgress = this._initialScrollProgress; } - this._webview.value.mountTo(this.container); + webview.mountTo(this.container); // Forward events from inner webview to outer listeners this._webviewEvents.clear(); diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts index 4577a20a046..eb930086188 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts @@ -6,12 +6,12 @@ import * as dom from 'vs/base/browser/dom'; import { memoize } from 'vs/base/common/decorators'; import { Lazy } from 'vs/base/common/lazy'; -import { UnownedDisposable as Unowned } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { EditorInput, EditorModel, GroupIdentifier, IEditorInput, Verbosity } from 'vs/workbench/common/editor'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { Emitter } from 'vs/base/common/event'; const WebviewPanelResourceScheme = 'webview-panel'; @@ -70,20 +70,31 @@ export class WebviewInput extends EditorInput { private _name: string; private _iconPath?: { light: URI, dark: URI }; private _group?: GroupIdentifier; + private readonly _webview: Lazy; + private _didSomeoneTakeMyWebview = false; + + private readonly _onDisposeWebview = this._register(new Emitter()); + readonly onDisposeWebview = this._onDisposeWebview.event; constructor( public readonly id: string, public readonly viewType: string, name: string, - webview: Lazy>, + webview: Lazy, @ILifecycleService private readonly lifecycleService: ILifecycleService, ) { super(); - this._name = name; + this._webview = webview; + } - this._webview = webview.map(value => this._register(value.acquire())); // The input owns this webview + dispose() { + super.dispose(); + if (!this._didSomeoneTakeMyWebview) { + this._webview?.rawValue?.dispose(); + this._onDisposeWebview.fire(); + } } public getTypeId(): string { @@ -119,7 +130,7 @@ export class WebviewInput extends EditorInput { } public get extension() { - return this._webview.getValue().extension; + return this.webview.extension; } public get iconPath() { @@ -150,4 +161,12 @@ export class WebviewInput extends EditorInput { public supportsSplitEditor() { return false; } + + protected takeOwnershipOfWebview(): WebviewEditorOverlay | undefined { + if (this._didSomeoneTakeMyWebview) { + return undefined; + } + this._didSomeoneTakeMyWebview = true; + return this.webview; + } } diff --git a/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts b/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts index ee635fbe60a..67011891137 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts @@ -6,7 +6,7 @@ import { equals } from 'vs/base/common/arrays'; import { memoize } from 'vs/base/common/decorators'; import { Lazy } from 'vs/base/common/lazy'; -import { IDisposable, toDisposable, UnownedDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { values } from 'vs/base/common/map'; import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; @@ -108,7 +108,7 @@ export class LazilyResolvedWebviewEditorInput extends WebviewInput { id: string, viewType: string, name: string, - webview: Lazy>, + webview: Lazy, @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, @ILifecycleService lifeCycleService: ILifecycleService, ) { @@ -161,7 +161,7 @@ export class WebviewEditorService implements IWebviewWorkbenchService { options: WebviewInputOptions, extension: WebviewExtensionDescription | undefined, ): WebviewInput { - const webview = new Lazy(() => new UnownedDisposable(this.createWebiew(id, extension, options))); + const webview = new Lazy(() => this.createWebiew(id, extension, options)); const webviewInput = this._instantiationService.createInstance(WebviewInput, id, viewType, title, webview); this._editorService.openEditor(webviewInput, { pinned: true, @@ -206,7 +206,7 @@ export class WebviewEditorService implements IWebviewWorkbenchService { const webview = new Lazy(() => { const webview = this.createWebiew(id, extension, options); webview.state = state; - return new UnownedDisposable(webview); + return webview; }); const webviewInput = this._instantiationService.createInstance(LazilyResolvedWebviewEditorInput, id, viewType, title, webview); From f3d70dbf921f3018fc742974efa1dfe6d8794ef7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Sat, 23 Nov 2019 15:00:16 -0800 Subject: [PATCH 41/58] Working on sharing models for same custom editor --- src/vs/workbench/api/browser/mainThreadWebview.ts | 13 +++++++++---- src/vs/workbench/api/common/extHost.protocol.ts | 4 ++-- src/vs/workbench/api/common/extHostWebview.ts | 2 +- .../contrib/customEditor/common/customEditor.ts | 6 +++--- .../customEditor/common/customEditorModel.ts | 13 +++++++------ 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 1ba9ed80383..99c2aaeff52 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -273,8 +273,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma const model = await this._customEditorService.models.loadOrCreate(webviewInput.getResource(), webviewInput.viewType); - model.onUndo(edits => { this._proxy.$undoEdits(handle, edits); }); - model.onRedo(edits => { this._proxy.$redoEdits(handle, edits); }); + model.onUndo(edits => { this._proxy.$undoEdits(handle, edits.map(x => x.data)); }); + model.onApplyEdit(edits => { + const editsToApply = edits.filter(x => x.source !== webviewInput).map(x => x.data); + if (editsToApply.length) { + this._proxy.$applyEdits(handle, editsToApply); + } + }); model.onWillSave(e => { e.waitUntil(this._proxy.$onSave(handle)); }); model.onWillSaveAs(e => { e.waitUntil(this._proxy.$onSaveAs(handle, e.resource.toJSON(), e.targetResource.toJSON())); }); @@ -309,7 +314,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._editorProviders.delete(viewType); } - public $onEdit(handle: extHostProtocol.WebviewPanelHandle, editData: string): void { + public $onEdit(handle: extHostProtocol.WebviewPanelHandle, editData: any): void { const webview = this.getWebviewInput(handle); if (!(webview instanceof CustomFileEditorInput)) { throw new Error('Webview is not a webview editor'); @@ -320,7 +325,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma throw new Error('Could not find model for webview editor'); } - model.makeEdit(editData); + model.makeEdit({ source: webview, data: editData }); } private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 72d18a284d4..fd400006266 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -574,7 +574,7 @@ export interface MainThreadWebviewsShape extends IDisposable { $registerEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void; $unregisterEditorProvider(viewType: string): void; - $onEdit(handle: WebviewPanelHandle, editJson: string): void; + $onEdit(handle: WebviewPanelHandle, editJson: any): void; } export interface WebviewPanelViewStateData { @@ -595,7 +595,7 @@ export interface ExtHostWebviewsShape { $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; $undoEdits(handle: WebviewPanelHandle, edits: readonly any[]): void; - $redoEdits(handle: WebviewPanelHandle, edits: readonly any[]): void; + $applyEdits(handle: WebviewPanelHandle, edits: readonly any[]): void; $onSave(handle: WebviewPanelHandle): Promise; $onSaveAs(handle: WebviewPanelHandle, resource: UriComponents, targetResource: UriComponents): Promise; diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index d1081d0f11e..68405468573 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -457,7 +457,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { panel?._undoEdits(edits); } - $redoEdits(handle: WebviewPanelHandle, edits: string[]): void { + $applyEdits(handle: WebviewPanelHandle, edits: string[]): void { const panel = this.getWebviewPanel(handle); panel?._redoEdits(edits); } diff --git a/src/vs/workbench/contrib/customEditor/common/customEditor.ts b/src/vs/workbench/contrib/customEditor/common/customEditor.ts index 2e08c304d7a..087e4e9a17d 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditor.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditor.ts @@ -38,7 +38,7 @@ export interface ICustomEditorService { promptOpenWith(resource: URI, options?: ITextEditorOptions, group?: IEditorGroup): Promise; } -export type CustomEditorEdit = unknown; +export type CustomEditorEdit = { source?: any, data: any }; export interface ICustomEditorModelManager { get(resource: URI, viewType: string): ICustomEditorModel | undefined; @@ -61,7 +61,7 @@ export interface CustomEditorSaveAsEvent { export interface ICustomEditorModel extends IWorkingCopy { readonly onUndo: Event; - readonly onRedo: Event; + readonly onApplyEdit: Event; readonly onWillSave: Event; readonly onWillSaveAs: Event; @@ -72,7 +72,7 @@ export interface ICustomEditorModel extends IWorkingCopy { save(options?: ISaveOptions): Promise; saveAs(resource: URI, targetResource: URI, currentOptions?: ISaveOptions): Promise; - makeEdit(data: string): void; + makeEdit(edit: CustomEditorEdit): void; } export const enum CustomEditorPriority { diff --git a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts index 2b36d9569d2..df42f0c3f0c 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts @@ -44,8 +44,8 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel protected readonly _onUndo = this._register(new Emitter()); readonly onUndo = this._onUndo.event; - protected readonly _onRedo = this._register(new Emitter()); - readonly onRedo = this._onRedo.event; + protected readonly _onApplyEdit = this._register(new Emitter()); + readonly onApplyEdit = this._onApplyEdit.event; protected readonly _onWillSave = this._register(new Emitter()); readonly onWillSave = this._onWillSave.event; @@ -53,10 +53,11 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel protected readonly _onWillSaveAs = this._register(new Emitter()); readonly onWillSaveAs = this._onWillSaveAs.event; - public makeEdit(data: string): void { - this._edits.splice(this._currentEditIndex + 1, this._edits.length - this._currentEditIndex, data); + public makeEdit(edit: CustomEditorEdit): void { + this._edits.splice(this._currentEditIndex + 1, this._edits.length - this._currentEditIndex, edit.data); this._currentEditIndex = this._edits.length - 1; this.updateDirty(); + this._onApplyEdit.fire([edit]); } private updateDirty() { @@ -114,7 +115,7 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel this._onUndo.fire(editsToUndo.reverse()); } else if (this._currentEditIndex < this._savePoint) { const editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint); - this._onRedo.fire(editsToRedo); + this._onApplyEdit.fire(editsToRedo); } this._currentEditIndex = this._savePoint; @@ -144,7 +145,7 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel ++this._currentEditIndex; const redoneEdit = this._edits[this._currentEditIndex]; - this._onRedo.fire([redoneEdit]); + this._onApplyEdit.fire([redoneEdit]); this.updateDirty(); } From 750f28769d9e878d5485a2f2fd06af5ad159d753 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 25 Nov 2019 11:44:08 -0800 Subject: [PATCH 42/58] Sync custom editor edits If multiple instances of the same custom editor are opened for the same resource, the edit stack should be shared between them. This matches how we work with text files --- src/vs/workbench/api/browser/mainThreadWebview.ts | 3 ++- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostWebview.ts | 13 +++++++++---- .../customEditor/browser/customEditorInput.ts | 5 +++++ .../contrib/customEditor/common/customEditor.ts | 2 ++ .../customEditor/common/customEditorModel.ts | 4 ++++ 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 99c2aaeff52..e9889040ffc 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -289,7 +289,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma try { await this._proxy.$resolveWebviewEditor( - webviewInput.getResource(), + { resource: webviewInput.getResource(), edits: model.currentEdits }, handle, viewType, webviewInput.getTitle(), @@ -299,6 +299,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } catch (error) { onUnexpectedError(error); webviewInput.webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType); + return; } } })); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index fd400006266..414ce05d910 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -592,7 +592,7 @@ export interface ExtHostWebviewsShape { $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; - $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; + $resolveWebviewEditor(input: { resource: UriComponents, edits: readonly any[] }, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; $undoEdits(handle: WebviewPanelHandle, edits: readonly any[]): void; $applyEdits(handle: WebviewPanelHandle, edits: readonly any[]): void; diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 68405468573..bbe3fe187d0 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -248,11 +248,11 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa } } - _undoEdits(edits: string[]): void { + _undoEdits(edits: readonly any[]): void { assertIsDefined(this._capabilities).editingCapability?.undoEdits(edits); } - _redoEdits(edits: string[]): void { + _redoEdits(edits: readonly any[]): void { assertIsDefined(this._capabilities).editingCapability?.applyEdits(edits); } @@ -432,7 +432,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { } async $resolveWebviewEditor( - resource: UriComponents, + input: { resource: UriComponents, edits: readonly any[] }, handle: WebviewPanelHandle, viewType: string, title: string, @@ -448,8 +448,13 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, this.workspace, extension, this._logService); const revivedPanel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); this._webviewPanels.set(handle, revivedPanel); - const capabilities = await provider.resolveWebviewEditor({ resource: URI.revive(resource) }, revivedPanel); + const capabilities = await provider.resolveWebviewEditor({ resource: URI.revive(input.resource) }, revivedPanel); revivedPanel._setCapabilities(capabilities); + + // TODO: the first set of edits should likely be passed when resolving + if (input.edits.length) { + revivedPanel._redoEdits(input.edits); + } } $undoEdits(handle: WebviewPanelHandle, edits: string[]): void { diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index a6d5a037799..673d8f3b96d 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -52,6 +52,10 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { return this._editorResource; } + public supportsSplitEditor() { + return true; + } + @memoize getName(): string { return basename(this.labelService.getUriLabel(this.getResource())); @@ -142,6 +146,7 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { public async resolve(): Promise { this._model = await this.customEditorService.models.loadOrCreate(this.getResource(), this.viewType); this._register(this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire())); + this._onDidChangeDirty.fire(); return await super.resolve(); } diff --git a/src/vs/workbench/contrib/customEditor/common/customEditor.ts b/src/vs/workbench/contrib/customEditor/common/customEditor.ts index 087e4e9a17d..9162ab0e3d2 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditor.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditor.ts @@ -65,6 +65,8 @@ export interface ICustomEditorModel extends IWorkingCopy { readonly onWillSave: Event; readonly onWillSaveAs: Event; + readonly currentEdits: readonly CustomEditorEdit[]; + undo(): void; redo(): void; revert(options?: IRevertOptions): Promise; diff --git a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts index df42f0c3f0c..bbf355c0377 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts @@ -53,6 +53,10 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel protected readonly _onWillSaveAs = this._register(new Emitter()); readonly onWillSaveAs = this._onWillSaveAs.event; + get currentEdits(): readonly CustomEditorEdit[] { + return this._edits.slice(0, Math.max(0, this._currentEditIndex + 1)); + } + public makeEdit(edit: CustomEditorEdit): void { this._edits.splice(this._currentEditIndex + 1, this._edits.length - this._currentEditIndex, edit.data); this._currentEditIndex = this._edits.length - 1; From 85d3ccfb9e0e02cbebbad19133639438306612fd Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 25 Nov 2019 11:54:14 -0800 Subject: [PATCH 43/58] Fix custom editor redo --- src/vs/workbench/api/common/extHostWebview.ts | 4 ++-- .../contrib/customEditor/common/customEditorModel.ts | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index bbe3fe187d0..e59a0eebbe7 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -457,12 +457,12 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { } } - $undoEdits(handle: WebviewPanelHandle, edits: string[]): void { + $undoEdits(handle: WebviewPanelHandle, edits: readonly any[]): void { const panel = this.getWebviewPanel(handle); panel?._undoEdits(edits); } - $applyEdits(handle: WebviewPanelHandle, edits: string[]): void { + $applyEdits(handle: WebviewPanelHandle, edits: readonly any[]): void { const panel = this.getWebviewPanel(handle); panel?._redoEdits(edits); } diff --git a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts index bbf355c0377..1443db0b0ce 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts @@ -14,7 +14,7 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel private _currentEditIndex: number = -1; private _savePoint: number = -1; - private _edits: Array = []; + private _edits: Array = []; constructor( private readonly _resource: URI, @@ -136,7 +136,7 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel const undoneEdit = this._edits[this._currentEditIndex]; --this._currentEditIndex; - this._onUndo.fire([undoneEdit]); + this._onUndo.fire([{ data: undoneEdit }]); this.updateDirty(); } @@ -149,7 +149,8 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel ++this._currentEditIndex; const redoneEdit = this._edits[this._currentEditIndex]; - this._onApplyEdit.fire([redoneEdit]); + + this._onApplyEdit.fire([{ data: redoneEdit }]); this.updateDirty(); } From 28ba6987d3d5034df7c660c30ecd925fa81e9efc Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 26 Nov 2019 00:26:26 +0000 Subject: [PATCH 44/58] Fix subpixel AA for settings TOC list Fix #85106 --- .../preferences/browser/media/settingsEditor2.css | 12 +----------- .../contrib/preferences/browser/settingsTree.ts | 2 ++ .../workbench/contrib/preferences/browser/tocTree.ts | 6 +++--- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 82ac9658052..894f925ba71 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -216,14 +216,13 @@ overflow: hidden; text-overflow: ellipsis; line-height: 22px; - opacity: 0.9; flex-shrink: 1; } .settings-editor > .settings-body .settings-toc-container .monaco-list-row .settings-toc-count { display: none; line-height: 22px; - opacity: 0.7; + opacity: 0.8; margin-left: 3px; } @@ -231,17 +230,8 @@ display: block; } -.settings-editor > .settings-body .settings-toc-container .monaco-list-row .monaco-tl-twistie { - opacity: 0.9; -} - -.settings-editor > .settings-body .settings-toc-container .monaco-list-row.selected .monaco-tl-twistie { - opacity: 1; -} - .settings-editor > .settings-body .settings-toc-container .monaco-list-row.selected .settings-toc-entry { font-weight: bold; - opacity: 1; } .settings-editor > .settings-body .settings-tree-container { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index e6fbc1582d8..d52e1403ed8 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1481,6 +1481,8 @@ export class SettingsTree extends ObjectTree { // applying an opacity to the link color. const fgWithOpacity = new Color(new RGBA(foregroundColor.rgba.r, foregroundColor.rgba.g, foregroundColor.rgba.b, 0.9)); collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description { color: ${fgWithOpacity}; }`); + + collector.addRule(`.settings-editor > .settings-body .settings-toc-container .monaco-list-row:not(.selected) { color: ${fgWithOpacity}; }`); } const errorColor = theme.getColor(errorForeground); diff --git a/src/vs/workbench/contrib/preferences/browser/tocTree.ts b/src/vs/workbench/contrib/preferences/browser/tocTree.ts index cb7b19f19f2..1120b48feca 100644 --- a/src/vs/workbench/contrib/preferences/browser/tocTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/tocTree.ts @@ -10,7 +10,7 @@ import { IObjectTreeOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTr import { ITreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { Iterator } from 'vs/base/common/iterator'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { editorBackground, transparent, foreground } from 'vs/platform/theme/common/colorRegistry'; import { attachStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { SettingsTreeFilter } from 'vs/workbench/contrib/preferences/browser/settingsTree'; @@ -215,8 +215,8 @@ export class TOCTree extends ObjectTree { listFocusAndSelectionBackground: editorBackground, listFocusAndSelectionForeground: settingsHeaderForeground, listFocusBackground: editorBackground, - listFocusForeground: settingsHeaderForeground, - listHoverForeground: settingsHeaderForeground, + listFocusForeground: transparent(foreground, 0.9), + listHoverForeground: transparent(foreground, 0.9), listHoverBackground: editorBackground, listInactiveSelectionBackground: editorBackground, listInactiveSelectionForeground: settingsHeaderForeground, From d86abd8e6025bc62837dc6272c649fd28b9c6418 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 25 Nov 2019 17:41:14 -0800 Subject: [PATCH 45/58] allow spaces in search result file names --- src/vs/workbench/contrib/search/browser/searchEditor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchEditor.ts b/src/vs/workbench/contrib/search/browser/searchEditor.ts index 5c78887001c..b89078a5bee 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditor.ts @@ -227,7 +227,7 @@ export const refreshActiveEditorSearch = export const createEditorFromSearchResult = async (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, labelService: ILabelService, editorService: IEditorService) => { - const searchTerm = searchResult.query?.contentPattern.pattern.replace(/[^\w-_.]/g, '') || 'Search'; + const searchTerm = searchResult.query?.contentPattern.pattern.replace(/[^\w-_. ]/g, '') || 'Search'; const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true }); From 7c9a63f6b6aa3cf921caa991ee82d35232e1e349 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 25 Nov 2019 18:46:34 -0800 Subject: [PATCH 46/58] Add search-editor specific find match highlight tokens --- src/vs/platform/theme/common/colorRegistry.ts | 8 ++++++++ .../contrib/search/browser/searchEditor.ts | 16 ++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 4c3093b823d..864db693fd1 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -299,6 +299,14 @@ export const editorFindMatchBorder = registerColor('editor.findMatchBorder', { l export const editorFindMatchHighlightBorder = registerColor('editor.findMatchHighlightBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('findMatchHighlightBorder', "Border color of the other search matches.")); export const editorFindRangeHighlightBorder = registerColor('editor.findRangeHighlightBorder', { dark: null, light: null, hc: transparent(activeContrastBorder, 0.4) }, nls.localize('findRangeHighlightBorder', "Border color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations."), true); +/** + * Search Editor query match colors. + * + * Distinct from normal editor find match to allow for better differentiation + */ +export const searchEditorFindMatch = registerColor('searchEditor.findMatchBackground', { light: transparent(editorFindMatchHighlight, 0.5), dark: transparent(editorFindMatchHighlight, 0.5), hc: editorFindMatchHighlight }, nls.localize('searchEditor.queryMatch', "Color of the Search Editor query matches.")); +export const searchEditorFindMatchBorder = registerColor('searchEditor.findMatchBorder', { light: transparent(editorFindMatchHighlightBorder, 0.5), dark: transparent(editorFindMatchHighlightBorder, 0.5), hc: editorFindMatchHighlightBorder }, nls.localize('searchEditor.editorFindMatchBorder', "Border color of the Search Editor query matches.")); + /** * Editor hover */ diff --git a/src/vs/workbench/contrib/search/browser/searchEditor.ts b/src/vs/workbench/contrib/search/browser/searchEditor.ts index b89078a5bee..4f515b013b1 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditor.ts @@ -18,6 +18,8 @@ import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/sea import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { searchEditorFindMatch, searchEditorFindMatchBorder } from 'vs/platform/theme/common/colorRegistry'; // Using \r\n on Windows inserts an extra newline between results. const lineDelimiter = '\n'; @@ -221,7 +223,7 @@ export const refreshActiveEditorSearch = const results = serializeSearchResultForEditor(searchModel.searchResult, '', '', labelFormatter); textModel.setValue(results.text.join(lineDelimiter)); - textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'findMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); + textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); }; @@ -250,5 +252,15 @@ export const createEditorFromSearchResult = const model = control.getModel() as ITextModel; - model.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'findMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); + model.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); }; + +// theming +registerThemingParticipant((theme, collector) => { + collector.addRule(`.monaco-editor .searchEditorFindMatch { background-color: ${theme.getColor(searchEditorFindMatch)}; }`); + + const findMatchHighlightBorder = theme.getColor(searchEditorFindMatchBorder); + if (findMatchHighlightBorder) { + collector.addRule(`.monaco-editor .searchEditorFindMatch { border: 1px ${theme.type === 'hc' ? 'dotted' : 'solid'} ${findMatchHighlightBorder}; box-sizing: border-box; }`); + } +}); From 52bdec0d4ef4375eb8ba8771b1ebb922cdfd1c97 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 25 Nov 2019 18:51:33 -0800 Subject: [PATCH 47/58] Make "open in search editor" keybinding only apply to mac --- src/vs/workbench/contrib/search/browser/search.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 5851978e65d..fea95600589 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -639,7 +639,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.create(ClearSearchResultsA registry.registerWorkbenchAction( SyncActionDescriptor.create(OpenResultsInEditorAction, OpenResultsInEditorAction.ID, OpenResultsInEditorAction.LABEL, - { primary: KeyMod.CtrlCmd | KeyCode.Enter }, + { mac: { primary: KeyMod.CtrlCmd | KeyCode.Enter } }, ContextKeyExpr.and(Constants.HasSearchResults, Constants.SearchViewFocusedKey, Constants.EnableSearchEditorPreview)), 'Search: Open Results in Editor', category, ContextKeyExpr.and(Constants.EnableSearchEditorPreview)); From d4ab1fcda583992478b48ed093f873296afeb81f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 25 Nov 2019 19:30:21 -0800 Subject: [PATCH 48/58] Fixes more node 12 typing errors (#85420) * Fixes more node 12 typing errors For #82514 * Remove Symbol.toStringTag usage for now * Reverting a few fixes that are not comptible with current node typings * Revert one more use of StringDecoder Must wait until we actually pick up the new typings --- src/vs/editor/contrib/gotoError/gotoError.ts | 2 +- src/vs/platform/request/node/proxy.ts | 4 ++-- src/vs/workbench/api/browser/mainThreadWorkspace.ts | 8 ++++---- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostDebugService.ts | 2 +- src/vs/workbench/api/common/extHostQuickOpen.ts | 2 +- src/vs/workbench/api/common/extHostTreeViews.ts | 2 +- src/vs/workbench/contrib/debug/browser/debugService.ts | 3 ++- src/vs/workbench/contrib/files/browser/fileActions.ts | 2 +- .../contrib/tasks/browser/abstractTaskService.ts | 2 +- src/vs/workbench/contrib/tasks/common/taskService.ts | 2 +- .../configuration/common/configurationEditingService.ts | 2 +- src/vs/workbench/services/search/node/rawSearchService.ts | 2 +- 13 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 10cff5b6510..f9ac8b712e8 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -43,7 +43,7 @@ class MarkerModel { this._markers = []; this._nextIdx = -1; this._ignoreSelectionChange = false; - this._onCurrentMarkerChanged = new Emitter(); + this._onCurrentMarkerChanged = new Emitter(); this._onMarkerSetChanged = new Emitter(); this.setMarkers(markers); diff --git a/src/vs/platform/request/node/proxy.ts b/src/vs/platform/request/node/proxy.ts index 30b5bc29a9a..d71addc2248 100644 --- a/src/vs/platform/request/node/proxy.ts +++ b/src/vs/platform/request/node/proxy.ts @@ -41,10 +41,10 @@ export async function getProxyAgent(rawRequestURL: string, options: IOptions = { host: proxyEndpoint.hostname || '', port: proxyEndpoint.port || (proxyEndpoint.protocol === 'https' ? '443' : '80'), auth: proxyEndpoint.auth, - rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true + rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true, }; return requestURL.protocol === 'http:' - ? new (await import('http-proxy-agent'))(opts) + ? new (await import('http-proxy-agent'))(opts as any as Url) : new (await import('https-proxy-agent'))(opts); } diff --git a/src/vs/workbench/api/browser/mainThreadWorkspace.ts b/src/vs/workbench/api/browser/mainThreadWorkspace.ts index 1ad44491aa1..5ab449439fa 100644 --- a/src/vs/workbench/api/browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/browser/mainThreadWorkspace.ts @@ -155,11 +155,11 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { if (!isPromiseCanceledError(err)) { return Promise.reject(err); } - return undefined; + return null; }); } - $startTextSearch(pattern: IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise { + $startTextSearch(pattern: IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise { const workspace = this._contextService.getWorkspace(); const folders = workspace.folders.map(folder => folder.uri); @@ -198,14 +198,14 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return this._searchService.fileSearch(query, token).then( result => { - return result.limitHit; + return !!result.limitHit; }, err => { if (!isPromiseCanceledError(err)) { return Promise.reject(err); } - return undefined; + return false; }); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 414ce05d910..cd622a40305 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -618,7 +618,7 @@ export interface ITextSearchComplete { export interface MainThreadWorkspaceShape extends IDisposable { $startFileSearch(includePattern: string | null, includeFolder: UriComponents | null, excludePatternOrDisregardExcludes: string | false | null, maxResults: number | null, token: CancellationToken): Promise; - $startTextSearch(query: search.IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise; + $startTextSearch(query: search.IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise; $checkExists(folders: UriComponents[], includes: string[], token: CancellationToken): Promise; $saveAll(includeUntitled?: boolean): Promise; $updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string; }[]): Promise; diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 75bc3851448..ea2d1da1b3d 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -378,7 +378,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb public async $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise { if (!this._variableResolver) { const [workspaceFolders, configProvider] = await Promise.all([this._workspaceService.getWorkspaceFolders2(), this._configurationService.getConfigProvider()]); - this._variableResolver = this.createVariableResolver(workspaceFolders || [], this._editorsService, configProvider); + this._variableResolver = this.createVariableResolver(workspaceFolders || [], this._editorsService, configProvider!); } let ws: IWorkspaceFolder | undefined; const folder = await this.getFolder(folderUri); diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index 8d089783698..aa41e81b660 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -135,7 +135,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { // ---- input - showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Promise { + showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Promise { // global validate fn used in callback below this._validateInput = options ? options.validateInput : undefined; diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 54cc945d6ae..5fa7306fdf9 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -419,7 +419,7 @@ class ExtHostTreeView extends Disposable { // check if an ancestor of extElement is already in the elements to update list let currentNode: TreeNode | undefined = elementNode; while (currentNode && currentNode.parent && !elementsToUpdate.has(currentNode.parent.item.handle)) { - const parentElement = this.elements.get(currentNode.parent.item.handle); + const parentElement: T | undefined = this.elements.get(currentNode.parent.item.handle); currentNode = parentElement ? this.nodes.get(parentElement) : undefined; } if (currentNode && !currentNode.parent) { diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 24a3b0902e9..d7378c6a497 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -47,6 +47,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { withUndefinedAsNull } from 'vs/base/common/types'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint'; @@ -807,7 +808,7 @@ export class DebugService implements IDebugService { return inactivePromise; } - return taskPromise; + return taskPromise.then(withUndefinedAsNull); }); return new Promise((c, e) => { diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 0e1d4b4801a..3bf6af89aa6 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -275,7 +275,7 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi }); }); - return servicePromise; + return servicePromise.then(undefined); }); }); } diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 987d0ad21a6..b77f7278b72 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -699,7 +699,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); } - public run(task: Task | undefined, options?: ProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System): Promise { + public run(task: Task | undefined, options?: ProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System): Promise { if (!task) { throw new TaskError(Severity.Info, nls.localize('TaskServer.noTask', 'Task to execute is undefined'), TaskErrors.TaskNotFound); } diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index 374c2e6141d..21ed4fa7174 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -58,7 +58,7 @@ export interface ITaskService { configureAction(): Action; build(): Promise; runTest(): Promise; - run(task: Task | undefined, options?: ProblemMatcherRunOptions): Promise; + run(task: Task | undefined, options?: ProblemMatcherRunOptions): Promise; inTerminal(): boolean; isActive(): Promise; getActiveTasks(): Promise; diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index d0113b72bd7..55662338cdb 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -158,7 +158,7 @@ export class ConfigurationEditingService { writeConfiguration(target: EditableConfigurationTarget, value: IConfigurationValue, options: IConfigurationEditingOptions = {}): Promise { const operation = this.getConfigurationEditOperation(target, value, options.scopes || {}); return Promise.resolve(this.queue.queue(() => this.doWriteConfiguration(operation, options) // queue up writes to prevent race conditions - .then(() => null, + .then(() => { }, error => { if (!options.donotNotifyError) { this.onError(error, operation, options.scopes); diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index c0d0e6d6358..04fb0cf5e1f 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -383,7 +383,7 @@ export class SearchService implements IRawSearchService { cancel() { // Do nothing } - then(resolve: any, reject: any) { + then(resolve?: ((value: C) => TResult1 | Promise) | undefined | null, reject?: ((reason: any) => TResult2 | Promise) | undefined | null): Promise { return promise.then(resolve, reject); } catch(reject?: any) { From 352d641042094036eb25635defb0f0122ff4feed Mon Sep 17 00:00:00 2001 From: ZHAO Jinxiang Date: Tue, 26 Nov 2019 11:46:09 +0800 Subject: [PATCH 49/58] Add .cjs to known JavaScript file extensions (#85460) --- build/lib/electron.ts | 2 +- build/win32/code.iss | 7 +++++++ extensions/javascript/package.json | 1 + .../markdown-basics/syntaxes/markdown.tmLanguage.json | 4 ++-- extensions/typescript-basics/build/update-grammars.js | 2 +- src/vs/base/test/node/glob.test.ts | 6 +++--- .../test/electron-browser/extensionsTipsService.test.ts | 2 +- 7 files changed, 16 insertions(+), 8 deletions(-) diff --git a/build/lib/electron.ts b/build/lib/electron.ts index 90a25f4cac8..86c7afcf312 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -59,7 +59,7 @@ export const config = { darwinBundleDocumentType(["asp", "aspx", "cshtml", "htm", "html", "jshtm", "jsp", "phtml", "shtml"], 'resources/darwin/html.icns'), darwinBundleDocumentType(["jade"], 'resources/darwin/jade.icns'), darwinBundleDocumentType(["jav", "java"], 'resources/darwin/java.icns'), - darwinBundleDocumentType(["js", "jscsrc", "jshintrc", "mjs"], 'resources/darwin/javascript.icns'), + darwinBundleDocumentType(["js", "jscsrc", "jshintrc", "mjs", "cjs"], 'resources/darwin/javascript.icns'), darwinBundleDocumentType(["json"], 'resources/darwin/json.icns'), darwinBundleDocumentType(["less"], 'resources/darwin/less.icns'), darwinBundleDocumentType(["markdown", "md", "mdoc", "mdown", "mdtext", "mdtxt", "mdwn", "mkd", "mkdn"], 'resources/darwin/markdown.icns'), diff --git a/build/win32/code.iss b/build/win32/code.iss index 0e2ed70d64a..54bde39080a 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -182,6 +182,13 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cjs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cjs\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.cjs"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles + Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clj\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clj\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.clj"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Clojure}"; Flags: uninsdeletekey; Tasks: associatewithfiles diff --git a/extensions/javascript/package.json b/extensions/javascript/package.json index 8d315c45835..b7303520549 100644 --- a/extensions/javascript/package.json +++ b/extensions/javascript/package.json @@ -32,6 +32,7 @@ ".js", ".es6", ".mjs", + ".cjs", ".pac" ], "filenames": [ diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index 0e2578d4bdd..477fff23131 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -957,7 +957,7 @@ ] }, "fenced_code_block_js": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(js|jsx|javascript|es6|mjs|\\{\\.js.+?\\})((\\s+|:|\\{)[^`~]*)?$)", + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(js|jsx|javascript|es6|mjs|cjs|\\{\\.js.+?\\})((\\s+|:|\\{)[^`~]*)?$)", "name": "markup.fenced_code.block.markdown", "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", "beginCaptures": { @@ -2623,4 +2623,4 @@ "name": "markup.inline.raw.string.markdown" } } -} \ No newline at end of file +} diff --git a/extensions/typescript-basics/build/update-grammars.js b/extensions/typescript-basics/build/update-grammars.js index 527fa2c1d43..1839ad63674 100644 --- a/extensions/typescript-basics/build/update-grammars.js +++ b/extensions/typescript-basics/build/update-grammars.js @@ -33,7 +33,7 @@ function patchGrammar(grammar) { function adaptToJavaScript(grammar, replacementScope) { grammar.name = 'JavaScript (with React support)'; - grammar.fileTypes = ['.js', '.jsx', '.es6', '.mjs']; + grammar.fileTypes = ['.js', '.jsx', '.es6', '.mjs', '.cjs']; grammar.scopeName = `source${replacementScope}`; var fixScopeNames = function (rule) { diff --git a/src/vs/base/test/node/glob.test.ts b/src/vs/base/test/node/glob.test.ts index f2610a7ee5f..f7e8dfd1356 100644 --- a/src/vs/base/test/node/glob.test.ts +++ b/src/vs/base/test/node/glob.test.ts @@ -14,12 +14,12 @@ suite('Glob', () => { // let patterns = [ // '{**/*.cs,**/*.json,**/*.csproj,**/*.sln}', // '{**/*.cs,**/*.csproj,**/*.sln}', - // '{**/*.ts,**/*.tsx,**/*.js,**/*.jsx,**/*.es6,**/*.mjs}', + // '{**/*.ts,**/*.tsx,**/*.js,**/*.jsx,**/*.es6,**/*.mjs,**/*.cjs}', // '**/*.go', // '{**/*.ps,**/*.ps1}', // '{**/*.c,**/*.cpp,**/*.h}', // '{**/*.fsx,**/*.fsi,**/*.fs,**/*.ml,**/*.mli}', - // '{**/*.js,**/*.jsx,**/*.es6,**/*.mjs}', + // '{**/*.js,**/*.jsx,**/*.es6,**/*.mjs,**/*.cjs}', // '{**/*.ts,**/*.tsx}', // '{**/*.php}', // '{**/*.php}', @@ -1015,4 +1015,4 @@ suite('Glob', () => { assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); } }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts index 3324f94e082..e12f352f072 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -206,7 +206,7 @@ suite('ExtensionsTipsService Test', () => { ...{ extensionTips: { 'ms-vscode.csharp': '{**/*.cs,**/project.json,**/global.json,**/*.csproj,**/*.sln,**/appsettings.json}', - 'msjsdiag.debugger-for-chrome': '{**/*.ts,**/*.tsx**/*.js,**/*.jsx,**/*.es6,**/.babelrc}', + 'msjsdiag.debugger-for-chrome': '{**/*.ts,**/*.tsx,**/*.js,**/*.jsx,**/*.es6,**/*.mjs,**/*.cjs,**/.babelrc}', 'lukehoban.Go': '**/*.go' }, extensionImportantTips: { From 4116c9411b21d8090f6ec3f0383971a1aa992597 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 25 Nov 2019 19:49:32 -0800 Subject: [PATCH 50/58] Use enum member completion type --- .../typescript-language-features/src/features/completions.ts | 2 ++ extensions/typescript-language-features/src/protocol.const.ts | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index dde8a7f2f68..0ffd0e637e4 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -169,6 +169,8 @@ class MyCompletionItem extends vscode.CompletionItem { return vscode.CompletionItemKind.Method; case PConst.Kind.enum: return vscode.CompletionItemKind.Enum; + case PConst.Kind.enumMember: + return vscode.CompletionItemKind.EnumMember; case PConst.Kind.module: case PConst.Kind.externalModuleName: return vscode.CompletionItemKind.Module; diff --git a/extensions/typescript-language-features/src/protocol.const.ts b/extensions/typescript-language-features/src/protocol.const.ts index efded538f77..7a6a7d70235 100644 --- a/extensions/typescript-language-features/src/protocol.const.ts +++ b/extensions/typescript-language-features/src/protocol.const.ts @@ -12,6 +12,7 @@ export class Kind { public static readonly constructSignature = 'construct'; public static readonly directory = 'directory'; public static readonly enum = 'enum'; + public static readonly enumMember = 'enum member'; public static readonly externalModuleName = 'external module name'; public static readonly function = 'function'; public static readonly indexSignature = 'index'; @@ -69,4 +70,4 @@ export class DisplayPartKind { public static readonly propertyName = 'propertyName'; public static readonly punctuation = 'punctuation'; public static readonly text = 'text'; -} \ No newline at end of file +} From dd300e4fe77f13d0410121aba1a4290d45435acf Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 25 Nov 2019 19:50:22 -0800 Subject: [PATCH 51/58] Show constants as normal variables Fixes #85429 --- .../typescript-language-features/src/features/completions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index 0ffd0e637e4..71d11085d4f 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -150,7 +150,6 @@ class MyCompletionItem extends vscode.CompletionItem { case PConst.Kind.keyword: return vscode.CompletionItemKind.Keyword; case PConst.Kind.const: - return vscode.CompletionItemKind.Constant; case PConst.Kind.let: case PConst.Kind.variable: case PConst.Kind.localVariable: From 16fffb122b1db1d48ea7fc462e8b66a5c121e074 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 25 Nov 2019 20:09:10 -0800 Subject: [PATCH 52/58] Mark parameter suggestions as variables For #85430 --- .../typescript-language-features/src/features/completions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index 71d11085d4f..da13f97f3db 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -154,6 +154,7 @@ class MyCompletionItem extends vscode.CompletionItem { case PConst.Kind.variable: case PConst.Kind.localVariable: case PConst.Kind.alias: + case PConst.Kind.parameter: return vscode.CompletionItemKind.Variable; case PConst.Kind.memberVariable: case PConst.Kind.memberGetAccessor: From ab05d797699a2306acc9450452875c719ef637a4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 25 Nov 2019 20:12:32 -0800 Subject: [PATCH 53/58] Fix node 12 typing error --- src/vs/platform/auth/electron-browser/authServer.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/auth/electron-browser/authServer.ts b/src/vs/platform/auth/electron-browser/authServer.ts index b2f227114fd..dae70743ad9 100644 --- a/src/vs/platform/auth/electron-browser/authServer.ts +++ b/src/vs/platform/auth/electron-browser/authServer.ts @@ -8,6 +8,7 @@ import * as url from 'url'; import * as fs from 'fs'; import * as net from 'net'; import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { assertIsDefined } from 'vs/base/common/types'; interface Deferred { resolve: (result: T | Promise) => void; @@ -25,7 +26,7 @@ export function createTerminateServer(server: http.Server) { }); }); return async () => { - const result = new Promise(resolve => server.close(resolve)); + const result = new Promise(resolve => server.close(resolve)); for (const id in sockets) { sockets[id].destroy(); } @@ -50,7 +51,7 @@ export async function startServer(server: http.Server): Promise { if (typeof address === 'string') { resolve(address); } else { - resolve(address.port.toString()); + resolve(assertIsDefined(address).port.toString()); } }); From a63d88ec550107a66c08d813fb5d3c32c9e131a7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 25 Nov 2019 20:58:47 -0800 Subject: [PATCH 54/58] Make sure we normalize paths before sending them to tsserver Fixes #84826 --- .../src/typescriptServiceClient.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index c77c86f7c9a..f45cb960dca 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -575,11 +575,15 @@ export default class TypeScriptServiceClient extends Disposable implements IType return undefined; } - const result = resource.fsPath; + let result = resource.fsPath; if (!result) { return undefined; } + if (resource.scheme === fileSchemes.file) { + result = path.normalize(result); + } + // Both \ and / must be escaped in regular expressions return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/'); } From 457cef96b796545b01167ddb7a00a4d51322d8e9 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 25 Nov 2019 12:43:38 -0500 Subject: [PATCH 55/58] Adds mailmap --- .mailmap | 1 + 1 file changed, 1 insertion(+) create mode 100644 .mailmap diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000000..3f53300caa4 --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +Eric Amodio Eric Amodio From e1edd6b4ad73ec07ac447b6ddfd5b04fd3804bce Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Tue, 26 Nov 2019 00:07:24 -0500 Subject: [PATCH 56/58] Fixes hr's in hovers because of the fix for #69388 This keeps hr's full-width (because otherwise they look very strange) --- src/vs/editor/contrib/hover/hover.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/editor/contrib/hover/hover.css b/src/vs/editor/contrib/hover/hover.css index 3e34dec5e19..684a3f64e5c 100644 --- a/src/vs/editor/contrib/hover/hover.css +++ b/src/vs/editor/contrib/hover/hover.css @@ -29,6 +29,10 @@ word-wrap: break-word; } +.monaco-editor-hover .markdown-hover > .hover-contents:not(.code-hover-contents) hr { + min-width: 100vw; +} + .monaco-editor-hover p, .monaco-editor-hover ul { margin: 8px 0; From 5ec2e40b21a4cd5ce6a5ed4eb3f509dc280a0d53 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 25 Nov 2019 22:37:22 -0800 Subject: [PATCH 57/58] Fixing webview getting disposed after rename For #83961 --- .../workbench/api/browser/mainThreadWebview.ts | 16 +++++++++++----- .../customEditor/browser/customEditorInput.ts | 5 +++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index e9889040ffc..4dd8f594288 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { onUnexpectedError } from 'vs/base/common/errors'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isWeb } from 'vs/base/common/platform'; import { startsWith } from 'vs/base/common/strings'; @@ -330,14 +330,20 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) { - input.webview.onDidClickLink((uri: URI) => this.onDidClickLink(handle, uri)); - input.webview.onMessage((message: any) => this._proxy.$onMessage(handle, message)); - input.onDisposeWebview(() => { + const disposables = new DisposableStore(); + + disposables.add(input.webview.onDidClickLink((uri: URI) => this.onDidClickLink(handle, uri))); + disposables.add(input.webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); })); + disposables.add(input.onDisposeWebview(() => { this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { this._webviewInputs.delete(handle); }); + })); + disposables.add(input.webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value))); + + input.onDispose(() => { + disposables.dispose(); }); - input.webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value)); } private updateWebviewViewStates() { diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 673d8f3b96d..78cba9eadda 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -7,7 +7,9 @@ import { memoize } from 'vs/base/common/decorators'; import { Lazy } from 'vs/base/common/lazy'; import { basename } from 'vs/base/common/path'; import { isEqual } from 'vs/base/common/resources'; +import { assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IEditorModel, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -18,7 +20,6 @@ import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/c import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { assertIsDefined } from 'vs/base/common/types'; export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { @@ -163,7 +164,7 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { return this.instantiationService.createInstance(CustomFileEditorInput, uri, this.viewType, - this.id, + generateUuid(), new Lazy(() => webview)); } } From dc5bff3872de82c2b5faf3d166eeaa83cedffa9d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 26 Nov 2019 09:13:54 +0100 Subject: [PATCH 58/58] #85036 extension kind ui should run only on ui --- .../extensions/browser/extensionsActions.ts | 4 + .../extensionsActions.test.ts | 103 +++++++++++------- .../common/extensionEnablementService.ts | 10 +- .../extensionEnablementService.test.ts | 6 +- 4 files changed, 72 insertions(+), 51 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index d3e7af804db..38c1765c955 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1253,6 +1253,10 @@ export class ReloadAction extends ExtensionAction { this.label = localize('reloadRequired', "Reload Required"); this.tooltip = localize('postUpdateTooltip', "Please reload Visual Studio Code to enable the updated extension."); } + } else { + this.enabled = true; + this.label = localize('reloadRequired', "Reload Required"); + this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to enable this extension."); } } } else { diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 5b2d45f343e..915b6cc7fae 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -1463,7 +1463,7 @@ suite('ExtensionsActions Test', () => { assert.equal(testObject.tooltip, 'Please reload Visual Studio Code to enable this extension.'); }); - test('Test ReloadAction is disabled when remote ui extension is installed in local server', async () => { + test('Test ReloadAction when ui extension is disabled on remote server and installed in local server', async () => { // multi server setup const gallery = aGalleryExtension('a'); const localExtensionManagementService = createExtensionManagementService([]); @@ -1478,7 +1478,7 @@ suite('ExtensionsActions Test', () => { const onDidChangeExtensionsEmitter: Emitter = new Emitter(); instantiationService.stub(IExtensionService, >{ - getExtensions: () => Promise.resolve([ExtensionsActions.toExtensionDescription(remoteExtension)]), + getExtensions: () => Promise.resolve([]), onDidChangeExtensions: onDidChangeExtensionsEmitter.event, canAddExtension: (extension) => false }); @@ -1495,7 +1495,8 @@ suite('ExtensionsActions Test', () => { const localExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file('pub.a') }); onDidInstallEvent.fire({ identifier: localExtension.identifier, local: localExtension, operation: InstallOperation.Install }); - assert.ok(!testObject.enabled); + assert.ok(testObject.enabled); + assert.equal(testObject.tooltip, 'Please reload Visual Studio Code to enable this extension.'); }); test('Test ReloadAction for remote ui extension is disabled when it is installed and enabled in local server', async () => { @@ -1529,37 +1530,6 @@ suite('ExtensionsActions Test', () => { assert.ok(!testObject.enabled); }); - test('Test ReloadAction for local ui extension is disabled when it is installed and enabled in remote server', async () => { - // multi server setup - const gallery = aGalleryExtension('a'); - const localExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file('pub.a') }); - const localExtensionManagementService = createExtensionManagementService([localExtension]); - const onDidInstallEvent = new Emitter(); - localExtensionManagementService.onDidInstallExtension = onDidInstallEvent.event; - const remoteExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file('pub.a').with({ scheme: Schemas.vscodeRemote }) }); - const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, localExtensionManagementService, createExtensionManagementService([remoteExtension])); - instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); - instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); - const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService); - instantiationService.set(IExtensionsWorkbenchService, workbenchService); - - const onDidChangeExtensionsEmitter: Emitter = new Emitter(); - instantiationService.stub(IExtensionService, >{ - getExtensions: () => Promise.resolve([ExtensionsActions.toExtensionDescription(remoteExtension)]), - onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => false - }); - const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); - instantiationService.createInstance(ExtensionContainers, [testObject]); - instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); - - await workbenchService.queryGallery(CancellationToken.None); - const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!); - testObject.extension = extensions[0]; - assert.ok(testObject.extension); - assert.ok(!testObject.enabled); - }); - test('Test remote install action is enabled for local workspace extension', async () => { // multi server setup const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) }); @@ -1913,7 +1883,7 @@ suite('ExtensionsActions Test', () => { assert.ok(!testObject.enabled); }); - test('Test local install action is disabled for remote ui extension', async () => { + test('Test local install action is enabled for remote ui extension', async () => { // multi server setup const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUIExtension])); @@ -1929,7 +1899,9 @@ suite('ExtensionsActions Test', () => { const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); await workbenchService.queryGallery(CancellationToken.None); testObject.extension = extensions[0]; - assert.ok(!testObject.enabled); + assert.ok(testObject.enabled); + assert.equal('Install Locally', testObject.label); + assert.equal('extension-action prominent install', testObject.class); }); test('Test local install action when installing remote ui extension', async () => { @@ -1953,10 +1925,54 @@ suite('ExtensionsActions Test', () => { const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); await workbenchService.queryGallery(CancellationToken.None); testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + assert.equal('Install Locally', testObject.label); + assert.equal('extension-action prominent install', testObject.class); + + onInstallExtension.fire({ identifier: remoteUIExtension.identifier, gallery }); + assert.ok(testObject.enabled); + assert.equal('Installing', testObject.label); + assert.equal('extension-action install installing', testObject.class); + }); + + test('Test local install action when installing remote ui extension is finished', async () => { + // multi server setup + const localExtensionManagementService: IExtensionManagementService = createExtensionManagementService(); + const onInstallExtension = new Emitter(); + localExtensionManagementService.onInstallExtension = onInstallExtension.event; + const onDidInstallEvent = new Emitter(); + localExtensionManagementService.onDidInstallExtension = onDidInstallEvent.event; + const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, localExtensionManagementService, createExtensionManagementService([remoteUIExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stub(IExtensionsWorkbenchService, workbenchService, 'open', undefined); + instantiationService.set(IExtensionsWorkbenchService, workbenchService); + + const gallery = aGalleryExtension('a', { identifier: remoteUIExtension.identifier }); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction); + instantiationService.createInstance(ExtensionContainers, [testObject]); + + const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); + await workbenchService.queryGallery(CancellationToken.None); + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + assert.equal('Install Locally', testObject.label); + assert.equal('extension-action prominent install', testObject.class); + + onInstallExtension.fire({ identifier: remoteUIExtension.identifier, gallery }); + assert.ok(testObject.enabled); + assert.equal('Installing', testObject.label); + assert.equal('extension-action install installing', testObject.class); + + const installedExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`) }); + onDidInstallEvent.fire({ identifier: installedExtension.identifier, local: installedExtension, operation: InstallOperation.Install }); assert.ok(!testObject.enabled); }); - test('Test local install action is disabled for disabled remote ui extension', async () => { + test('Test local install action is enabled for disabled remote ui extension', async () => { // multi server setup const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUIExtension])); @@ -1973,7 +1989,9 @@ suite('ExtensionsActions Test', () => { const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); await workbenchService.queryGallery(CancellationToken.None); testObject.extension = extensions[0]; - assert.ok(!testObject.enabled); + assert.ok(testObject.enabled); + assert.equal('Install Locally', testObject.label); + assert.equal('extension-action prominent install', testObject.class); }); test('Test local install action is disabled when extension is not set', async () => { @@ -2073,7 +2091,7 @@ suite('ExtensionsActions Test', () => { assert.ok(!testObject.enabled); }); - test('Test local install action is disabled for remote UI extension if it uninstalled locally', async () => { + test('Test local install action is disabled for remoteUI extension if it is uninstalled locally', async () => { // multi server setup const extensionManagementService = instantiationService.get(IExtensionManagementService); const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), extensionManagementService); @@ -2091,13 +2109,14 @@ suite('ExtensionsActions Test', () => { const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); await workbenchService.queryGallery(CancellationToken.None); testObject.extension = extensions[0]; - assert.ok(!testObject.enabled); + assert.ok(testObject.enabled); + assert.equal('Install Locally', testObject.label); uninstallEvent.fire(remoteUIExtension.identifier); assert.ok(!testObject.enabled); }); - test('Test local install action is disabled for remote UI extension if it has gallery', async () => { + test('Test local install action is enabled for remote UI extension if it has gallery', async () => { // multi server setup const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUIExtension])); @@ -2113,7 +2132,7 @@ suite('ExtensionsActions Test', () => { const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); testObject.extension = extensions[0]; assert.ok(testObject.extension); - assert.ok(!testObject.enabled); + assert.ok(testObject.enabled); }); test('Test local install action is disabled for remote UI system extension', async () => { diff --git a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts index c64ba121c87..839923c54bc 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts @@ -15,7 +15,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { isUndefinedOrNull } from 'vs/base/common/types'; import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { canExecuteOnUI } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -147,11 +147,9 @@ export class ExtensionEnablementService extends Disposable implements IExtension private _isDisabledByExtensionKind(extension: IExtension): boolean { if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { - if (!canExecuteOnUI(extension.manifest, this.productService, this.configurationService)) { - // workspace extensions must run on the remote, but UI extensions can run on either side - const server = this.extensionManagementServerService.remoteExtensionManagementServer; - return this.extensionManagementServerService.getExtensionManagementServer(extension.location) !== server; - } + const extensionKind = getExtensionKind(extension.manifest, this.productService, this.configurationService); + const server = extensionKind[0] === 'ui' ? this.extensionManagementServerService.localExtensionManagementServer : this.extensionManagementServerService.remoteExtensionManagementServer; + return this.extensionManagementServerService.getExtensionManagementServer(extension.location) !== server; } return false; } diff --git a/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index 0e4e725ae64..ca0d803ed5d 100644 --- a/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -437,8 +437,8 @@ suite('ExtensionEnablementService Test', () => { instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService)); const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); testObject = new TestExtensionEnablementService(instantiationService); - assert.ok(testObject.isEnabled(localWorkspaceExtension)); - assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally); + assert.ok(!testObject.isEnabled(localWorkspaceExtension)); + assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.DisabledByExtensionKind); }); test('test remote workspace extension is not disabled by kind', async () => { @@ -453,7 +453,7 @@ suite('ExtensionEnablementService Test', () => { instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService)); const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); testObject = new TestExtensionEnablementService(instantiationService); - assert.equal(testObject.canChangeEnablement(localWorkspaceExtension), true); + assert.equal(testObject.canChangeEnablement(localWorkspaceExtension), false); }); test('test canChangeEnablement return true for remote workspace extension', () => {