From a563b5e5e5d5fd0265d376cda71f9f3ab075ac81 Mon Sep 17 00:00:00 2001 From: Ilya Murav'jov Date: Wed, 17 Jan 2018 15:05:41 +0300 Subject: [PATCH 001/843] editor.insertSpaceAfterComment setting --- src/vs/editor/common/config/commonEditorConfig.ts | 5 +++++ src/vs/editor/contrib/comment/comment.ts | 3 ++- .../editor/contrib/comment/lineCommentCommand.ts | 14 +++++++++----- .../comment/test/lineCommentCommand.test.ts | 13 ++++++++----- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 73339f9d245..4149e2093dc 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -643,6 +643,11 @@ const editorConfiguration: IConfigurationNode = { 'description': nls.localize('selectionClipboard', "Controls if the Linux primary clipboard should be supported."), 'included': platform.isLinux }, + 'editor.insertSpaceAfterComment': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('insertSpaceAfterComment', "Whether to insert space after comment character.") + }, 'diffEditor.renderSideBySide': { 'type': 'boolean', 'default': true, diff --git a/src/vs/editor/contrib/comment/comment.ts b/src/vs/editor/contrib/comment/comment.ts index a72eae7bc90..4f79eb765ba 100644 --- a/src/vs/editor/contrib/comment/comment.ts +++ b/src/vs/editor/contrib/comment/comment.ts @@ -12,6 +12,7 @@ import { registerEditorAction, IActionOptions, EditorAction, ServicesAccessor } import { BlockCommentCommand } from './blockCommentCommand'; import { LineCommentCommand, Type } from './lineCommentCommand'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; abstract class CommentLineAction extends EditorAction { @@ -33,7 +34,7 @@ abstract class CommentLineAction extends EditorAction { var opts = model.getOptions(); for (var i = 0; i < selections.length; i++) { - commands.push(new LineCommentCommand(selections[i], opts.tabSize, this._type)); + commands.push(new LineCommentCommand(selections[i], opts.tabSize, this._type, accessor.get(IConfigurationService))); } editor.pushUndoStop(); diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index 4a31a0424b7..f57f0f8fb6e 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -14,6 +14,7 @@ import { BlockCommentCommand } from './blockCommentCommand'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { CharCode } from 'vs/base/common/charCode'; import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export interface IInsertionPoint { ignore: boolean; @@ -52,7 +53,9 @@ export class LineCommentCommand implements editorCommon.ICommand { private _tabSize: number; private _type: Type; - constructor(selection: Selection, tabSize: number, type: Type) { + constructor(selection: Selection, tabSize: number, type: Type, + @IConfigurationService private _configurationService: IConfigurationService, + ) { this._selection = selection; this._tabSize = tabSize; this._type = type; @@ -198,7 +201,7 @@ export class LineCommentCommand implements editorCommon.ICommand { ops = LineCommentCommand._createRemoveLineCommentsOperations(data.lines, s.startLineNumber); } else { LineCommentCommand._normalizeInsertionPoint(model, data.lines, s.startLineNumber, this._tabSize); - ops = LineCommentCommand._createAddLineCommentsOperations(data.lines, s.startLineNumber); + ops = this._createAddLineCommentsOperations(data.lines, s.startLineNumber); } const cursorPosition = new Position(s.positionLineNumber, s.positionColumn); @@ -371,11 +374,12 @@ export class LineCommentCommand implements editorCommon.ICommand { /** * Generate edit operations in the add line comment case */ - public static _createAddLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): IIdentifiedSingleEditOperation[] { + public _createAddLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): IIdentifiedSingleEditOperation[] { var i: number, len: number, lineData: ILinePreflightData, - res: IIdentifiedSingleEditOperation[] = []; + res: IIdentifiedSingleEditOperation[] = [], + afterCommentStr = this._configurationService.getValue('editor.insertSpaceAfterComment') ? ' ' : ''; for (i = 0, len = lines.length; i < len; i++) { lineData = lines[i]; @@ -384,7 +388,7 @@ export class LineCommentCommand implements editorCommon.ICommand { continue; } - res.push(EditOperation.insert(new Position(startLineNumber + i, lineData.commentStrOffset + 1), lineData.commentStr + ' ')); + res.push(EditOperation.insert(new Position(startLineNumber + i, lineData.commentStrOffset + 1), lineData.commentStr + afterCommentStr)); } return res; diff --git a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts index e1e61fa2262..dbfaed3786c 100644 --- a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts @@ -15,18 +15,21 @@ import { TokenizationResult2 } from 'vs/editor/common/core/token'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; import { CommentRule } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; + +var mockConfigService = new TestConfigurationService(); suite('Editor Contrib - Line Comment Command', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#', blockComment: [''] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, mockConfigService), expectedLines, expectedSelection); mode.dispose(); } function testAddLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#', blockComment: [''] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd, mockConfigService), expectedLines, expectedSelection); mode.dispose(); } @@ -594,7 +597,7 @@ suite('Editor Contrib - Line Comment As Block Comment', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '', blockComment: ['(', ')'] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, mockConfigService), expectedLines, expectedSelection); mode.dispose(); } @@ -705,7 +708,7 @@ suite('Editor Contrib - Line Comment As Block Comment', () => { suite('Editor Contrib - Line Comment As Block Comment 2', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: null, blockComment: [''] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, mockConfigService), expectedLines, expectedSelection); mode.dispose(); } @@ -944,7 +947,7 @@ suite('Editor Contrib - Line Comment in mixed modes', () => { lines, outerMode.getLanguageIdentifier(), selection, - (sel) => new LineCommentCommand(sel, 4, Type.Toggle), + (sel) => new LineCommentCommand(sel, 4, Type.Toggle, mockConfigService), expectedLines, expectedSelection ); From 94f10a489f4006d2d534a75ee761b88082511752 Mon Sep 17 00:00:00 2001 From: Ilya Murav'jov Date: Tue, 30 Jan 2018 03:52:50 +0300 Subject: [PATCH 002/843] fix tests --- src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts index dbfaed3786c..7e6df5d47d1 100644 --- a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts @@ -18,6 +18,7 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; var mockConfigService = new TestConfigurationService(); +mockConfigService.setUserConfiguration('editor', { 'insertSpaceAfterComment': true }); suite('Editor Contrib - Line Comment Command', () => { From e8d3880269dfcc8adbfa4940afde77ebad7d794c Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Thu, 25 Jul 2019 01:27:59 -0500 Subject: [PATCH 003/843] Fix mightProducePrintableCharacter numeric keypad support. Fixes #71134 --- .../keybinding/browser/keybindingService.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 4e64ebdc0c6..ed4bde0a996 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -46,7 +46,7 @@ import { IKeymapService } from 'vs/workbench/services/keybinding/common/keymapIn import { getDispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; import { isArray } from 'vs/base/common/types'; import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/common/navigatorKeyboard'; -import { ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/base/common/scanCode'; +import { ScanCode, ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE, IMMUTABLE_KEY_CODE_TO_CODE } from 'vs/base/common/scanCode'; interface ContributedKeyBinding { command: string; @@ -535,6 +535,30 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { return false; } const code = ScanCodeUtils.toEnum(event.code); + + const NUMPAD_PRINTABLE_SCANCODES = [ + ScanCode.NumpadDivide, + ScanCode.NumpadMultiply, + ScanCode.NumpadSubtract, + ScanCode.NumpadAdd, + ScanCode.Numpad1, + ScanCode.Numpad2, + ScanCode.Numpad3, + ScanCode.Numpad4, + ScanCode.Numpad5, + ScanCode.Numpad6, + ScanCode.Numpad7, + ScanCode.Numpad8, + ScanCode.Numpad9, + ScanCode.Numpad0, + ScanCode.NumpadDecimal + ]; + const immutableScanCode = IMMUTABLE_KEY_CODE_TO_CODE[event.keyCode]; + if (NUMPAD_PRINTABLE_SCANCODES.indexOf(code) >= 0 && code === immutableScanCode) { + // code === immutableScanCode when NumLock is enabled + return true; + } + const keycode = IMMUTABLE_CODE_TO_KEY_CODE[code]; if (keycode !== -1) { // https://github.com/microsoft/vscode/issues/74934 From e0cb4e2542f28bacb25bb5c0f79fb1f921b41d84 Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Sat, 26 Oct 2019 18:48:25 -0500 Subject: [PATCH 004/843] :lipstick: --- .../keybinding/browser/keybindingService.ts | 82 ++++++++++--------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 6b789b49a32..91ed7d94a21 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -141,6 +141,24 @@ const keybindingsExtPoint = ExtensionsRegistry.registerExtensionPoint= 0 && code === immutableScanCode) { - // code === immutableScanCode when NumLock is enabled - return true; + if (NUMPAD_PRINTABLE_SCANCODES.indexOf(code) === -1) { + const keycode = IMMUTABLE_CODE_TO_KEY_CODE[code]; + if (keycode !== -1) { + // https://github.com/microsoft/vscode/issues/74934 + return false; + } + // consult the KeyboardMapperFactory to check the given event for + // a printable value. + const mapping = this.keymapService.getRawKeyboardMapping(); + if (!mapping) { + return false; + } + const keyInfo = mapping[event.code]; + if (!keyInfo) { + return false; + } + if (!keyInfo.value || /\s/.test(keyInfo.value)) { + return false; + } + } else { + const immutableScanCode = IMMUTABLE_KEY_CODE_TO_CODE[event.keyCode]; + if (code !== immutableScanCode) { + // NumLock is disabled + return false; + } } - const keycode = IMMUTABLE_CODE_TO_KEY_CODE[code]; - if (keycode !== -1) { - // https://github.com/microsoft/vscode/issues/74934 - return false; - } - // consult the KeyboardMapperFactory to check the given event for - // a printable value. - const mapping = this.keymapService.getRawKeyboardMapping(); - if (!mapping) { - return false; - } - const keyInfo = mapping[event.code]; - if (!keyInfo) { - return false; - } - if (!keyInfo.value || /\s/.test(keyInfo.value)) { - return false; - } return true; } } From 2e93b5726d5727be9c7db979f7325a702839bd62 Mon Sep 17 00:00:00 2001 From: Sameer <11097096+sameer@users.noreply.github.com> Date: Sat, 17 Nov 2018 22:24:29 -0600 Subject: [PATCH 005/843] Enable Shift-Insert to paste primary clipboard on Linux, fixes #36170. --- .../electron-browser/selectionClipboard.ts | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts b/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts index 65b603080a4..c6898134ef7 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts @@ -11,7 +11,7 @@ import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { Range } from 'vs/editor/common/core/range'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IEditorContribution, Handler } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; @@ -19,6 +19,8 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; export class SelectionClipboard extends Disposable implements IEditorContribution { private static readonly SELECTION_LENGTH_LIMIT = 65536; @@ -79,6 +81,30 @@ export class SelectionClipboard extends Disposable implements IEditorContributio } setSelectionToClipboard.schedule(); })); + + this._register(editor.onKeyDown((e: IKeyboardEvent) => { + if (!isEnabled) { + return; + } + + if (!editor.hasModel()) { + return; + } + + if (e.equals(KeyMod.Shift | KeyCode.Insert)) { + // prevent paste from 'clipboard' clipboard + e.preventDefault(); + + // trigger paste from 'primary' clipboard + clipboardService.readText('selection').then(text => { + editor.focus(); + editor.trigger('keyboard', Handler.Paste, { + text: text, + pasteOnNewLine: false + }); + }); + } + })); } } From 60c653ea6c7a5b940beab6c9498e000ebd838253 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 21 Nov 2019 18:42:47 +0100 Subject: [PATCH 006/843] wip: scm input editor --- .../contrib/scm/browser/media/scmViewlet.css | 12 +- .../contrib/scm/browser/repositoryPane.ts | 154 +++++++++++------- 2 files changed, 94 insertions(+), 72 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css index 1a56521f66e..f4dda15248f 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css +++ b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css @@ -171,16 +171,8 @@ display: none; } -.scm-viewlet .scm-editor > .monaco-inputbox { - width: 100%; -} - -.scm-viewlet .scm-editor > .monaco-inputbox > .wrapper > .mirror { - max-height: 134px; -} - -.scm-viewlet .scm-editor > .monaco-inputbox > .wrapper > textarea.input { - min-height: 26px; +.scm-viewlet .scm-editor-container { + padding: 3px 4px; } .scm-viewlet .scm-editor.scroll > .monaco-inputbox > .wrapper > textarea.input { diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index e4fa1369aed..ecc1d3b558b 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -5,13 +5,12 @@ import 'vs/css!./media/scmViewlet'; import { Event, Emitter } from 'vs/base/common/event'; -import { domEvent } from 'vs/base/browser/event'; import { basename, isEqual } from 'vs/base/common/resources'; import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; import { append, $, addClass, toggleClass, trackFocus, removeClass } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ISCMRepository, ISCMResourceGroup, ISCMResource, InputValidationType } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMRepository, ISCMResourceGroup, ISCMResource } from 'vs/workbench/contrib/scm/common/scm'; import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -27,14 +26,11 @@ import { SCMMenus } from './menus'; import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { IThemeService, LIGHT } from 'vs/platform/theme/common/themeService'; import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar } from './util'; -import { attachBadgeStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; -import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; -import { format } from 'vs/base/common/strings'; +import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ThrottledDelayer, disposableTimeout } from 'vs/base/common/async'; +import { disposableTimeout } from 'vs/base/common/async'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import * as platform from 'vs/base/common/platform'; import { ITreeNode, ITreeFilter, ITreeSorter, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { ResourceTree, IResourceNode } from 'vs/base/common/resourceTree'; import { ISequence, ISplice } from 'vs/base/common/sequence'; @@ -54,6 +50,11 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { Hasher } from 'vs/base/common/hash'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { ITextModel } from 'vs/editor/common/model'; +import { IEditorConstructionOptions } from 'vs/editor/common/config/editorOptions'; +import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; +import { IModelService } from 'vs/editor/common/services/modelService'; type TreeElement = ISCMResourceGroup | IResourceNode | ISCMResource; @@ -578,20 +579,21 @@ export class ToggleViewModeAction extends Action { } } -function convertValidationType(type: InputValidationType): MessageType { - switch (type) { - case InputValidationType.Information: return MessageType.INFO; - case InputValidationType.Warning: return MessageType.WARNING; - case InputValidationType.Error: return MessageType.ERROR; - } -} +// function convertValidationType(type: InputValidationType): MessageType { +// switch (type) { +// case InputValidationType.Information: return MessageType.INFO; +// case InputValidationType.Warning: return MessageType.WARNING; +// case InputValidationType.Error: return MessageType.ERROR; +// } +// } export class RepositoryPane extends ViewletPane { private cachedHeight: number | undefined = undefined; private cachedWidth: number | undefined = undefined; - private inputBoxContainer!: HTMLElement; - private inputBox!: InputBox; + private inputContainer!: HTMLElement; + private inputEditor!: CodeEditorWidget; + private inputModel!: ITextModel; private listContainer!: HTMLElement; private tree!: WorkbenchCompressibleObjectTree; private viewModel!: ViewModel; @@ -599,7 +601,7 @@ export class RepositoryPane extends ViewletPane { private menus: SCMMenus; private toggleViewModelModeAction: ToggleViewModeAction | undefined; protected contextKeyService: IContextKeyService; - private commitTemplate = ''; + // private commitTemplate = ''; constructor( readonly repository: ISCMRepository, @@ -615,7 +617,8 @@ export class RepositoryPane extends ViewletPane { @IConfigurationService protected configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, @IMenuService protected menuService: IMenuService, - @IStorageService private storageService: IStorageService + @IStorageService private storageService: IStorageService, + @IModelService private modelService: IModelService ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService); @@ -655,51 +658,76 @@ export class RepositoryPane extends ViewletPane { this._register(focusTracker); // Input - this.inputBoxContainer = append(container, $('.scm-editor')); + this.inputContainer = append(container, $('.scm-editor')); + const editorContainer = append(this.inputContainer, $('.scm-editor-container')); + // TODO@joao const updatePlaceholder = () => { - const binding = this.keybindingService.lookupKeybinding('scm.acceptInput'); - const label = binding ? binding.getLabel() : (platform.isMacintosh ? 'Cmd+Enter' : 'Ctrl+Enter'); - const placeholder = format(this.repository.input.placeholder, label); + // const binding = this.keybindingService.lookupKeybinding('scm.acceptInput'); + // const label = binding ? binding.getLabel() : (platform.isMacintosh ? 'Cmd+Enter' : 'Ctrl+Enter'); + // const placeholder = format(this.repository.input.placeholder, label); - this.inputBox.setPlaceHolder(placeholder); + // this.inputBox.setPlaceHolder(placeholder); }; - const validationDelayer = new ThrottledDelayer(200); - const validate = () => { - return this.repository.input.validateInput(this.inputBox.value, this.inputBox.inputElement.selectionStart || 0).then(result => { - if (!result) { - this.inputBox.inputElement.removeAttribute('aria-invalid'); - this.inputBox.hideMessage(); - } else { - this.inputBox.inputElement.setAttribute('aria-invalid', 'true'); - this.inputBox.showMessage({ content: result.message, type: convertValidationType(result.type) }); - } - }); + // const validationDelayer = new ThrottledDelayer(200); + // const validate = () => { + + // const position = this.inputEditor.getSelection()?.getStartPosition(); + // const offset = position && this.inputModel.getOffsetAt(position); + // const value = this.inputModel.getValue(); + + // return this.repository.input.validateInput(value, offset || 0).then(result => { + + // // TODO@joao + // if (!result) { + // // this.inputBox.inputElement.removeAttribute('aria-invalid'); + // // this.inputBox.hideMessage(); + // } else { + // // this.inputBox.inputElement.setAttribute('aria-invalid', 'true'); + // // this.inputBox.showMessage({ content: result.message, type: convertValidationType(result.type) }); + // } + // }); + // }; + + // const triggerValidation = () => validationDelayer.trigger(validate); + + const editorOptions: IEditorConstructionOptions = { + ...getSimpleEditorOptions() + }; + const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { + isSimpleWidget: true }; - const triggerValidation = () => validationDelayer.trigger(validate); + this.inputEditor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, editorOptions, codeEditorWidgetOptions); - this.inputBox = new InputBox(this.inputBoxContainer, this.contextViewService, { flexibleHeight: true, flexibleMaxHeight: 134 }); - this.inputBox.setEnabled(this.isBodyVisible()); - this._register(attachInputBoxStyler(this.inputBox, this.themeService)); - this._register(this.inputBox); + // this.inputBox = new InputBox(this.inputBoxContainer, this.contextViewService, { flexibleHeight: true, flexibleMaxHeight: 134 }); + // this.inputBox.setEnabled(this.isBodyVisible()); + // this._register(attachInputBoxStyler(this.inputBox, this.themeService)); + this._register(this.inputEditor); - this._register(this.inputBox.onDidChange(triggerValidation, null)); + this._register(this.inputEditor.onDidFocusEditorText(() => addClass(editorContainer, 'synthetic-focus'))); + this._register(this.inputEditor.onDidBlurEditorText(() => removeClass(editorContainer, 'synthetic-focus'))); - const onKeyUp = domEvent(this.inputBox.inputElement, 'keyup'); - const onMouseUp = domEvent(this.inputBox.inputElement, 'mouseup'); - this._register(Event.any(onKeyUp, onMouseUp)(triggerValidation, null)); + let resource = URI.parse('scm://input'); + this.inputModel = this.modelService.createModel('', null, resource, true); + this.inputEditor.setModel(this.inputModel); - this.inputBox.value = this.repository.input.value; - this._register(this.inputBox.onDidChange(value => this.repository.input.value = value, null)); - this._register(this.repository.input.onDidChange(value => this.inputBox.value = value, null)); + // this._register(this.inputBox.onDidChange(triggerValidation, null)); + + // const onKeyUp = domEvent(this.inputBox.inputElement, 'keyup'); + // const onMouseUp = domEvent(this.inputBox.inputElement, 'mouseup'); + // this._register(Event.any(onKeyUp, onMouseUp)(triggerValidation, null)); + + // this.inputBox.value = this.repository.input.value; + // this._register(this.inputBox.onDidChange(value => this.repository.input.value = value, null)); + // this._register(this.repository.input.onDidChange(value => this.inputBox.value = value, null)); updatePlaceholder(); this._register(this.repository.input.onDidChangePlaceholder(updatePlaceholder, null)); this._register(this.keybindingService.onDidUpdateKeybindings(updatePlaceholder, null)); - this._register(this.inputBox.onDidHeightChange(() => this.layoutBody())); + // this._register(this.inputBox.onDidHeightChange(() => this.layoutBody())); if (this.repository.provider.onDidChangeCommitTemplate) { this._register(this.repository.provider.onDidChangeCommitTemplate(this.onDidChangeCommitTemplate, this)); @@ -822,15 +850,16 @@ export class RepositoryPane extends ViewletPane { this.cachedHeight = height; if (this.repository.input.visible) { - removeClass(this.inputBoxContainer, 'hidden'); - this.inputBox.layout(); + removeClass(this.inputContainer, 'hidden'); + this.inputEditor.layout({ height: 38, width: width! - 12 - 16 - 2 }); // TODO@joao - const editorHeight = this.inputBox.height; + const editorHeight = 40; // TODO@joao const listHeight = height - (editorHeight + 12 /* margin */); this.listContainer.style.height = `${listHeight}px`; this.tree.layout(listHeight, width); } else { - addClass(this.inputBoxContainer, 'hidden'); + addClass(this.inputContainer, 'hidden'); + // TODO@joao: disable editor this.listContainer.style.height = `${height}px`; this.tree.layout(height, width); @@ -842,7 +871,7 @@ export class RepositoryPane extends ViewletPane { if (this.isExpanded()) { if (this.repository.input.visible) { - this.inputBox.focus(); + this.inputEditor.focus(); } else { this.tree.domFocus(); } @@ -852,7 +881,7 @@ export class RepositoryPane extends ViewletPane { } private _onDidChangeVisibility(visible: boolean): void { - this.inputBox.setEnabled(visible); + // this.inputEditor.setEnabled(visible); this.viewModel.setVisible(visible); } @@ -929,19 +958,20 @@ export class RepositoryPane extends ViewletPane { .filter(r => !!r && !isSCMResourceGroup(r))! as any; } + // TODO@joao private onDidChangeCommitTemplate(): void { - if (typeof this.repository.provider.commitTemplate === 'undefined' || !this.repository.input.visible) { - return; - } + // if (typeof this.repository.provider.commitTemplate === 'undefined' || !this.repository.input.visible) { + // return; + // } - const oldCommitTemplate = this.commitTemplate; - this.commitTemplate = this.repository.provider.commitTemplate; + // const oldCommitTemplate = this.commitTemplate; + // this.commitTemplate = this.repository.provider.commitTemplate; - if (this.inputBox.value && this.inputBox.value !== oldCommitTemplate) { - return; - } + // if (this.inputBox.value && this.inputBox.value !== oldCommitTemplate) { + // return; + // } - this.inputBox.value = this.commitTemplate; + // this.inputBox.value = this.commitTemplate; } private updateInputBoxVisibility(): void { From f9a16cbf777f653a1fb0622b0a5d477055efc205 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 25 Nov 2019 12:26:01 +0100 Subject: [PATCH 007/843] 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 8300c92f0e77293301fd4ac68bcb17b033f98ec5 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 25 Nov 2019 15:42:29 +0100 Subject: [PATCH 008/843] improve editor padding --- .../contrib/scm/browser/media/scmViewlet.css | 2 +- .../contrib/scm/browser/repositoryPane.ts | 27 +++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css index f4dda15248f..48e93bc55af 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css +++ b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css @@ -172,7 +172,7 @@ } .scm-viewlet .scm-editor-container { - padding: 3px 4px; + padding: 1px; } .scm-viewlet .scm-editor.scroll > .monaco-inputbox > .wrapper > textarea.input { diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index ecc1d3b558b..e0c4894d807 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -55,6 +55,10 @@ import { ITextModel } from 'vs/editor/common/model'; import { IEditorConstructionOptions } from 'vs/editor/common/config/editorOptions'; import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; import { IModelService } from 'vs/editor/common/services/modelService'; +import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; +import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer'; +import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; +import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu'; type TreeElement = ISCMResourceGroup | IResourceNode | ISCMResource; @@ -693,10 +697,18 @@ export class RepositoryPane extends ViewletPane { // const triggerValidation = () => validationDelayer.trigger(validate); const editorOptions: IEditorConstructionOptions = { - ...getSimpleEditorOptions() + ...getSimpleEditorOptions(), + lineDecorationsWidth: 4, + dragAndDrop: false, + // fontFamily: 'Arial' }; const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { - isSimpleWidget: true + isSimpleWidget: true, + contributions: EditorExtensionsRegistry.getSomeEditorContributions([ + MenuPreventer.ID, + SelectionClipboardContributionID, + ContextMenuController.ID + ]) }; this.inputEditor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, editorOptions, codeEditorWidgetOptions); @@ -713,6 +725,10 @@ export class RepositoryPane extends ViewletPane { this.inputModel = this.modelService.createModel('', null, resource, true); this.inputEditor.setModel(this.inputModel); + this.inputEditor.changeViewZones(accessor => { + accessor.addZone({ afterLineNumber: 0, domNode: $('div'), heightInPx: 3 }); + }); + // this._register(this.inputBox.onDidChange(triggerValidation, null)); // const onKeyUp = domEvent(this.inputBox.inputElement, 'keyup'); @@ -851,10 +867,11 @@ export class RepositoryPane extends ViewletPane { if (this.repository.input.visible) { removeClass(this.inputContainer, 'hidden'); - this.inputEditor.layout({ height: 38, width: width! - 12 - 16 - 2 }); // TODO@joao + // const editorHeight = 25; // TODO@joao + const editorHeight = 250; // TODO@joao + this.inputEditor.layout({ height: editorHeight, width: width! - 12 - 16 - 2 /* - 8 */ }); // TODO@joao - const editorHeight = 40; // TODO@joao - const listHeight = height - (editorHeight + 12 /* margin */); + const listHeight = height - (editorHeight + 5 + 2 /* + 3 + 3 */ + 5); this.listContainer.style.height = `${listHeight}px`; this.tree.layout(listHeight, width); } else { From 34c591eeb39986a5aca0fdbae0a21df9289d9771 Mon Sep 17 00:00:00 2001 From: Charles Gagnon Date: Mon, 9 Dec 2019 09:37:52 -0800 Subject: [PATCH 009/843] Don't instrument any test code for coverage --- test/coverage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/coverage.js b/test/coverage.js index bf7e7aa3f95..2c4c12ffaaf 100644 --- a/test/coverage.js +++ b/test/coverage.js @@ -17,7 +17,7 @@ const REPO_PATH = toUpperDriveLetter(path.join(__dirname, '..')); exports.initialize = function (loaderConfig) { const instrumenter = iLibInstrument.createInstrumenter(); loaderConfig.nodeInstrumenter = function (contents, source) { - if (minimatch(source, '**/test/**/*.test.js')) { + if (minimatch(source, '**/test/**')) { // tests don't get instrumented return contents; } From fb2eca62c21d9112126835acf0c1b868f45019b2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 11 Dec 2019 15:39:45 -0800 Subject: [PATCH 010/843] Show empty image instead of error in git diff view for newly staged images Fixes #86389 Fixes #86776 Uses `fs.stat` to check if we are showing an untracked image in the git diff view. Also required fixing `stat` for `gitfs` so that it can try to return the proper sizes for objects --- extensions/git/src/fileSystemProvider.ts | 40 +++++++++++++++--------- extensions/image-preview/src/preview.ts | 37 +++++++++++----------- 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/extensions/git/src/fileSystemProvider.ts b/extensions/git/src/fileSystemProvider.ts index 198f031309f..9687dbf59de 100644 --- a/extensions/git/src/fileSystemProvider.ts +++ b/extensions/git/src/fileSystemProvider.ts @@ -8,6 +8,7 @@ import { debounce, throttle } from './decorators'; import { fromGitUri, toGitUri } from './uri'; import { Model, ModelChangeEvent, OriginalResourceChangeEvent } from './model'; import { filterEvent, eventToPromise, isDescendant, pathEquals, EmptyDisposable } from './util'; +import { Repository } from './repository'; interface CacheRow { uri: Uri; @@ -116,15 +117,21 @@ export class GitFileSystemProvider implements FileSystemProvider { return EmptyDisposable; } - stat(uri: Uri): FileStat { - const { submoduleOf } = fromGitUri(uri); + async stat(uri: Uri): Promise { + const { submoduleOf, path, ref } = fromGitUri(uri); const repository = submoduleOf ? this.model.getRepository(submoduleOf) : this.model.getRepository(uri); - if (!repository) { throw FileSystemError.FileNotFound(); } - return { type: FileType.File, size: 0, mtime: this.mtime, ctime: 0 }; + let size = 0; + try { + const details = await repository.getObjectDetails(this.fixRef(ref, path, repository), path); + size = details.size; + } catch { + // noop + } + return { type: FileType.File, size: size, mtime: this.mtime, ctime: 0 }; } readDirectory(): Thenable<[string, FileType][]> { @@ -136,7 +143,7 @@ export class GitFileSystemProvider implements FileSystemProvider { } async readFile(uri: Uri): Promise { - let { path, ref, submoduleOf } = fromGitUri(uri); + const { path, ref, submoduleOf } = fromGitUri(uri); if (submoduleOf) { const repository = this.model.getRepository(submoduleOf); @@ -165,17 +172,8 @@ export class GitFileSystemProvider implements FileSystemProvider { this.cache.set(uri.toString(), cacheValue); - if (ref === '~') { - const fileUri = Uri.file(path); - const uriString = fileUri.toString(); - const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); - ref = indexStatus ? '' : 'HEAD'; - } else if (/^~\d$/.test(ref)) { - ref = `:${ref[1]}`; - } - try { - return await repository.buffer(ref, path); + return await repository.buffer(this.fixRef(ref, path, repository), path); } catch (err) { return new Uint8Array(0); } @@ -196,4 +194,16 @@ export class GitFileSystemProvider implements FileSystemProvider { dispose(): void { this.disposables.forEach(d => d.dispose()); } + + private fixRef(ref: string, path: string, repository: Repository): string { + if (ref === '~') { + const fileUri = Uri.file(path); + const uriString = fileUri.toString(); + const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); + return indexStatus ? '' : 'HEAD'; + } else if (/^~\d$/.test(ref)) { + return `:${ref[1]}`; + } + return ref; + } } diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index 83fcaafaaaf..9ebbae8a744 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -75,6 +75,8 @@ class Preview extends Disposable { private _imageBinarySize: number | undefined; private _imageZoom: Scale | undefined; + private readonly emptyPngDataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAEElEQVR42gEFAPr/AP///wAI/AL+Sr4t6gAAAABJRU5ErkJggg=='; + constructor( private readonly extensionRoot: vscode.Uri, private readonly resource: vscode.Uri, @@ -167,9 +169,9 @@ class Preview extends Disposable { } } - private render() { + private async render() { if (this._previewState !== PreviewState.Disposed) { - this.webviewEditor.webview.html = this.getWebiewContents(); + this.webviewEditor.webview.html = await this.getWebiewContents(); } } @@ -193,11 +195,11 @@ class Preview extends Disposable { } } - private getWebiewContents(): string { + private async getWebiewContents(): Promise { const version = Date.now().toString(); const settings = { isMac: process.platform === 'darwin', - src: this.getResourcePath(this.webviewEditor, this.resource, version), + src: await this.getResourcePath(this.webviewEditor, this.resource, version), }; const nonce = Date.now().toString(); @@ -226,22 +228,19 @@ class Preview extends Disposable { `; } - private getResourcePath(webviewEditor: vscode.WebviewPanel, resource: vscode.Uri, version: string) { - switch (resource.scheme) { - case 'data': - return resource.toString(true); - - case 'git': - // Show blank image - return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAEElEQVR42gEFAPr/AP///wAI/AL+Sr4t6gAAAABJRU5ErkJggg=='; - - default: - // Avoid adding cache busting if there is already a query string - if (resource.query) { - return webviewEditor.webview.asWebviewUri(resource).toString(true); - } - return webviewEditor.webview.asWebviewUri(resource).with({ query: `version=${version}` }).toString(true); + private async getResourcePath(webviewEditor: vscode.WebviewPanel, resource: vscode.Uri, version: string): Promise { + if (resource.scheme === 'gitfs') { + const stat = await vscode.workspace.fs.stat(resource); + if (stat.size === 0) { + return this.emptyPngDataUri; + } } + + // Avoid adding cache busting if there is already a query string + if (resource.query) { + return webviewEditor.webview.asWebviewUri(resource).toString(true); + } + return webviewEditor.webview.asWebviewUri(resource).with({ query: `version=${version}` }).toString(true); } private extensionResource(path: string) { From a5be98587f1b3cb5241fc52695b77cb9afebce79 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 17 Dec 2019 18:49:38 +0100 Subject: [PATCH 011/843] implement tree things --- .../contrib/bulkEdit/browser/bulkEditTree.ts | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts new file mode 100644 index 00000000000..03c015bc851 --- /dev/null +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree'; +import * as modes from 'vs/editor/common/modes'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { FuzzyScore, createMatches } from 'vs/base/common/filters'; +import { IResourceLabel, ResourceLabels, DEFAULT_LABELS_CONTAINER } from 'vs/workbench/browser/labels'; +import { URI } from 'vs/base/common/uri'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { Range } from 'vs/editor/common/core/range'; +import * as dom from 'vs/base/browser/dom'; + +// --- VIEW MODEL + +export class FileElement { + + constructor(readonly edit: modes.ResourceFileEdit | modes.ResourceTextEdit) { } + + getUri(): URI { + return modes.isResourceTextEdit(this.edit) + ? this.edit.resource + : this.edit.oldUri || this.edit.newUri!; + } +} + +export class TextEditElement { + constructor(readonly line: string, readonly highlight: IHighlight, readonly edit: modes.TextEdit) { } +} + +export type Edit = FileElement | TextEditElement; + +// --- DATA SOURCE + +export class BulkEditDataSource implements IAsyncDataSource { + + constructor(@ITextModelService private readonly _textModelService: ITextModelService) { } + + hasChildren(element: modes.WorkspaceEdit | Edit): boolean { + if (element instanceof FileElement) { + return modes.isResourceTextEdit(element.edit); + } + if (element instanceof TextEditElement) { + return false; + } + return true; + } + + async getChildren(element: modes.WorkspaceEdit | Edit): Promise { + + // root -> file/text edits + if (Array.isArray((element).edits)) { + return (element).edits.map(edit => new FileElement(edit)); + } + + // file: text edit + if (element instanceof FileElement && modes.isResourceTextEdit(element.edit)) { + const ref = await this._textModelService.createModelReference(element.edit.resource); + const textModel = ref.object.textEditorModel; + + const result = element.edit.edits.map(edit => { + const range = Range.lift(edit.range); + const start = textModel.getOffsetAt(range.getStartPosition()); + const end = textModel.getOffsetAt(range.getEndPosition()); + const len = end - start; + + const previewStart = textModel.getPositionAt(start - 20); + const previewEnd = textModel.getPositionAt(end + 30); + + const preview = textModel.getValueInRange(Range.fromPositions(previewStart, previewEnd)); + const previewOffset = start - textModel.getOffsetAt(previewStart); + + return new TextEditElement(preview, { start: previewOffset, end: previewOffset + len }, edit); + }); + + return result; + } + + return []; + } +} + +// --- IDENT + +export class BulkEditIdentityProvider implements IIdentityProvider { + + private readonly _map = new WeakMap(); + private _idPool = 0; + + getId(element: Edit): { toString(): string; } { + let id = this._map.get(element); + if (typeof id === 'undefined') { + id = this._idPool++; + this._map.set(element, id); + } + return id; + } +} + +// --- RENDERER + +class FileElementTemplate { + constructor(readonly label: IResourceLabel) { } +} + +export class FileElementRenderer implements ITreeRenderer { + + static readonly id: string = 'FileElementRenderer'; + + readonly templateId: string = FileElementRenderer.id; + + private readonly _resourceLabel: ResourceLabels; + + constructor(@IInstantiationService instaService: IInstantiationService) { + this._resourceLabel = instaService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER); + } + + renderTemplate(container: HTMLElement): FileElementTemplate { + return new FileElementTemplate(this._resourceLabel.create(container, { supportHighlights: true })); + } + + renderElement(node: ITreeNode, _index: number, template: FileElementTemplate): void { + template.label.setFile(node.element.getUri(), { matches: createMatches(node.filterData) }); + } + + disposeTemplate(template: FileElementTemplate): void { + template.label.dispose(); + } +} + +class TextEditElementTemplate { + constructor(readonly label: HighlightedLabel) { } +} + +export class TextEditElementRenderer implements ITreeRenderer { + + static readonly id = 'TextEditElementRenderer'; + + readonly templateId: string = TextEditElementRenderer.id; + + renderTemplate(container: HTMLElement): TextEditElementTemplate { + const label = new HighlightedLabel(container, false); + dom.addClass(label.element, 'textedit'); + return new TextEditElementTemplate(label); + } + + renderElement(node: ITreeNode, _index: number, template: TextEditElementTemplate): void { + template.label.set(node.element.line, [node.element.highlight], undefined, true); + } + + disposeTemplate(_template: TextEditElementTemplate): void { } +} + +export class Delegate implements IListVirtualDelegate { + + getHeight(): number { + return 23; + } + + getTemplateId(element: Edit): string { + return element instanceof FileElement + ? FileElementRenderer.id + : TextEditElementRenderer.id; + } +} From 94a469319585ed5cce2e2d3fdd7e2bb740dacf3d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 18 Dec 2019 11:25:45 +0100 Subject: [PATCH 012/843] add BulkEditPreviewHandler --- .../browser/services/bulkEditService.ts | 6 +++++- .../standalone/browser/simpleServices.ts | 4 ++++ .../bulkEdit/browser/bulkEditService.ts | 21 ++++++++++++++++--- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index ada4ed7b23d..a21f7252590 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -7,10 +7,10 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { WorkspaceEdit } from 'vs/editor/common/modes'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; +import { IDisposable } from 'vs/base/common/lifecycle'; export const IBulkEditService = createDecorator('IWorkspaceEditService'); - export interface IBulkEditOptions { editor?: ICodeEditor; progress?: IProgress; @@ -20,9 +20,13 @@ export interface IBulkEditResult { ariaSummary: string; } +export type IBulkEditPreviewHandler = (edit: WorkspaceEdit, options?: IBulkEditOptions) => Promise; + export interface IBulkEditService { _serviceBrand: undefined; + setPreviewHandler(handler: IBulkEditPreviewHandler): IDisposable; + apply(edit: WorkspaceEdit, options?: IBulkEditOptions): Promise; } diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index b5180aa9a2a..463a6940a6d 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -631,6 +631,10 @@ export class SimpleBulkEditService implements IBulkEditService { // } + setPreviewHandler(): IDisposable { + return Disposable.None; + } + apply(workspaceEdit: WorkspaceEdit, options?: IBulkEditOptions): Promise { let edits = new Map(); diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index fd4c3bdaad9..40739d7055a 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { mergeSort } from 'vs/base/common/arrays'; -import { dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, IReference, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IBulkEditOptions, IBulkEditResult, IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { IBulkEditOptions, IBulkEditResult, IBulkEditService, IBulkEditPreviewHandler } from 'vs/editor/browser/services/bulkEditService'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; @@ -381,6 +381,8 @@ export class BulkEditService implements IBulkEditService { _serviceBrand: undefined; + private _previewHandler?: IBulkEditPreviewHandler; + constructor( @ILogService private readonly _logService: ILogService, @IModelService private readonly _modelService: IModelService, @@ -393,7 +395,20 @@ export class BulkEditService implements IBulkEditService { @IConfigurationService private readonly _configurationService: IConfigurationService ) { } - apply(edit: WorkspaceEdit, options: IBulkEditOptions = {}): Promise { + setPreviewHandler(handler: IBulkEditPreviewHandler): IDisposable { + this._previewHandler = handler; + return toDisposable(() => { + if (this._previewHandler === handler) { + this._previewHandler = undefined; + } + }); + } + + async apply(edit: WorkspaceEdit, options: IBulkEditOptions = {}): Promise { + + if (this._previewHandler) { + edit = await this._previewHandler(edit, options); + } let { edits } = edit; let codeEditor = options.editor; From ee9d42129da25fb1851bd4ab608fa2c24eb36d6e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 18 Dec 2019 12:08:06 +0100 Subject: [PATCH 013/843] first cut of panel and changes tree --- build/lib/i18n.resources.json | 4 + .../ui/highlightedlabel/highlightedLabel.ts | 3 +- .../bulkEdit/browser/bulkEdit.contribution.ts | 53 +++++++++ .../contrib/bulkEdit/browser/bulkEdit.css | 10 ++ .../contrib/bulkEdit/browser/bulkEditPanel.ts | 110 ++++++++++++++++++ .../bulkEdit/browser/bulkEditPreview.ts | 52 +++++++++ .../contrib/bulkEdit/browser/bulkEditTree.ts | 35 +++--- src/vs/workbench/workbench.common.main.ts | 3 + 8 files changed, 255 insertions(+), 15 deletions(-) create mode 100644 src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts create mode 100644 src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css create mode 100644 src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts create mode 100644 src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index e9a4f279631..b497e93f42f 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -30,6 +30,10 @@ "name": "vs/workbench/api/common", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/bulkEdit", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/cli", "project": "vscode-workbench" diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index 163dffb2d5a..9bb9292a4cd 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -10,6 +10,7 @@ import { escape } from 'vs/base/common/strings'; export interface IHighlight { start: number; end: number; + extraClasses?: string; } export class HighlightedLabel { @@ -69,7 +70,7 @@ export class HighlightedLabel { htmlContent += ''; pos = highlight.end; } - htmlContent += ''; + htmlContent += ``; const substring = this.text.substring(highlight.start, highlight.end); htmlContent += this.supportCodicons ? renderCodicons(escape(substring)) : escape(substring); htmlContent += ''; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts new file mode 100644 index 00000000000..1d6f1930587 --- /dev/null +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { WorkspaceEdit } from 'vs/editor/common/modes'; +import { BulkEditPanel } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPanel'; +import { Extensions as PanelExtensions, PanelDescriptor, PanelRegistry } from 'vs/workbench/browser/panel'; +import { localize } from 'vs/nls'; + +class BulkEditPreviewContribution { + + constructor( + @IPanelService private _panelService: IPanelService, + @IBulkEditService bulkEditService: IBulkEditService, + ) { + + bulkEditService.setPreviewHandler(edit => this._previewEdit(edit)); + } + + private async _previewEdit(edit: WorkspaceEdit) { + + const panel = this._panelService.openPanel(BulkEditPanel.ID, true); + if (!(panel instanceof BulkEditPanel)) { + // error? + return edit; + } + + const apply = await panel.setInput(edit); + if (!apply) { + return { edits: [] }; + } + // todo@joh get 'real' edit + return edit; + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + BulkEditPreviewContribution, LifecyclePhase.Ready +); + +Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create( + BulkEditPanel, + BulkEditPanel.ID, + localize('panel', "Refactor Preview"), + 'bulkEditPanel', + 10 +)); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css new file mode 100644 index 00000000000..c46eb7bd817 --- /dev/null +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .bulk-edit-panel .highlight.remove { + text-decoration: line-through; +} + + diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts new file mode 100644 index 00000000000..fa7add19ca6 --- /dev/null +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./bulkEdit'; +import { Panel } from 'vs/workbench/browser/panel'; +import { Dimension, addClass } from 'vs/base/browser/dom'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; +import { WorkspaceEdit } from 'vs/editor/common/modes'; +import { Edit, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; +import { FuzzyScore } from 'vs/base/common/filters'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { Action, IAction } from 'vs/base/common/actions'; +import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistry'; + +export class BulkEditPanel extends Panel { + + static readonly ID = 'BulkEditPanel'; + private static EmptyWorkspaceEdit = { edits: [] }; + + private _tree!: WorkbenchAsyncDataTree; + private _acceptAction: IAction; + private _discardAction: IAction; + + constructor( + @IInstantiationService private readonly _instaService: IInstantiationService, + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IStorageService storageService: IStorageService + ) { + super(BulkEditPanel.ID, telemetryService, themeService, storageService); + + this._acceptAction = new Action('ok', 'Apply', 'codicon-check', false, async () => this._done(true)); + this._discardAction = new Action('ok', 'Discard', 'codicon-trash', false, async () => this._done(false)); + } + + create(parent: HTMLElement): void { + super.create(parent); + + addClass(parent, 'bulk-edit-panel'); + + const treeContainer = document.createElement('div'); + treeContainer.style.width = '100%'; + treeContainer.style.height = '100%'; + parent.appendChild(treeContainer); + + this._tree = this._instaService.createInstance( + WorkbenchAsyncDataTree, this.getId(), treeContainer, + new BulkEditDelegate(), + [new TextEditElementRenderer(), this._instaService.createInstance(FileElementRenderer)], + this._instaService.createInstance(BulkEditDataSource), + { + identityProvider: new BulkEditIdentityProvider() + } + ); + } + + layout(dimension: Dimension): void { + this._tree.layout(dimension.height, dimension.width); + } + + private _currentResolve?: (apply: boolean) => void; + + setInput(edit: WorkspaceEdit): Promise { + + if (this._currentResolve) { + this._currentResolve(false); + this._currentResolve = undefined; + } + + this._acceptAction.enabled = true; + this._discardAction.enabled = true; + + return new Promise(async resolve => { + this._currentResolve = resolve; + await this._tree.setInput(edit); + this._tree.domFocus(); + this._tree.focusFirst(); + }); + } + + private _done(accept: boolean): void { + if (this._currentResolve) { + this._currentResolve(accept); + this._acceptAction.enabled = false; + this._discardAction.enabled = false; + this._tree.setInput(BulkEditPanel.EmptyWorkspaceEdit); + } + } + + getActions() { + return [this._acceptAction, this._discardAction]; + } +} + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + + const diffInsertedColor = theme.getColor(diffInserted); + if (diffInsertedColor) { + collector.addRule(`.monaco-workbench .bulk-edit-panel .highlight.insert { background-color: ${diffInsertedColor}; }`); + } + const diffRemovedColor = theme.getColor(diffRemoved); + if (diffRemovedColor) { + collector.addRule(`.monaco-workbench .bulk-edit-panel .highlight.remove { background-color: ${diffRemovedColor}; }`); + } +}); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts new file mode 100644 index 00000000000..c5fdb4f4ce1 --- /dev/null +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree'; +// import * as modes from 'vs/editor/common/modes'; +// import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; +// import { URI } from 'vs/base/common/uri'; +// import { ITextModel } from 'vs/editor/common/model'; +// import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +// import { IModeService } from 'vs/editor/common/services/modeService'; +// import { IModelService } from 'vs/editor/common/services/modelService'; +// import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; + +// export class BulkEditPreviewProvider implements ITextModelContentProvider { + +// static readonly Schema = 'vscode-bulkeditpreview'; + +// static asPreviewUri(uri: URI): URI { +// return URI.from({ scheme: BulkEditPreviewProvider.Schema, path: uri.toString() }); +// } + +// static fromPreviewUri(uri: URI): URI { +// return URI.parse(uri.path); +// } + +// constructor( +// @IModeService private readonly _modeService: IModeService, +// @IModelService private readonly _modelService: IModelService, +// @ITextModelService private readonly textModelResolverService: ITextModelService +// ) { +// this.textModelResolverService.registerTextModelContentProvider(BulkEditPreviewProvider.Schema, this); +// } + +// async provideTextContent(previewUri: URI) { + +// const resourceUri = BulkEditPreviewProvider.fromPreviewUri(previewUri); + +// const ref = await this.textModelResolverService.createModelReference(resourceUri); + +// const sourceModel = ref.object.textEditorModel; + +// const previewModel = this._modelService.createModel( +// createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), +// this._modeService.create(sourceModel.getLanguageIdentifier().language), +// previewUri +// ); + +// return null; +// } +// } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 03c015bc851..372366e9b0f 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -29,7 +29,7 @@ export class FileElement { } export class TextEditElement { - constructor(readonly line: string, readonly highlight: IHighlight, readonly edit: modes.TextEdit) { } + constructor(readonly edit: modes.TextEdit, readonly prefix: string, readonly selecting: string, readonly inserting: string, readonly suffix: string) { } } export type Edit = FileElement | TextEditElement; @@ -64,17 +64,14 @@ export class BulkEditDataSource implements IAsyncDataSource { const range = Range.lift(edit.range); - const start = textModel.getOffsetAt(range.getStartPosition()); - const end = textModel.getOffsetAt(range.getEndPosition()); - const len = end - start; - const previewStart = textModel.getPositionAt(start - 20); - const previewEnd = textModel.getPositionAt(end + 30); - - const preview = textModel.getValueInRange(Range.fromPositions(previewStart, previewEnd)); - const previewOffset = start - textModel.getOffsetAt(previewStart); - - return new TextEditElement(preview, { start: previewOffset, end: previewOffset + len }, edit); + return new TextEditElement( + edit, + textModel.getValueInRange(new Range(range.startLineNumber, 1, range.startLineNumber, range.startColumn)), // line start to edit start, + textModel.getValueInRange(range), + edit.text, + textModel.getValueInRange(new Range(range.endLineNumber, range.endColumn, range.endLineNumber, range.endColumn + 20)) + ); }); return result; @@ -148,14 +145,24 @@ export class TextEditElementRenderer implements ITreeRenderer, _index: number, template: TextEditElementTemplate): void { - template.label.set(node.element.line, [node.element.highlight], undefined, true); + renderElement({ element }: ITreeNode, _index: number, template: TextEditElementTemplate): void { + + let value = ''; + value += element.prefix; + value += element.selecting; + value += element.inserting; + value += element.suffix; + + let selectHighlight: IHighlight = { start: element.prefix.length, end: element.prefix.length + element.selecting.length, extraClasses: 'remove' }; + let insertHighlight: IHighlight = { start: selectHighlight.end, end: selectHighlight.end + element.inserting.length, extraClasses: 'insert' }; + + template.label.set(value, [selectHighlight, insertHighlight], undefined, true); } disposeTemplate(_template: TextEditElementTemplate): void { } } -export class Delegate implements IListVirtualDelegate { +export class BulkEditDelegate implements IListVirtualDelegate { getHeight(): number { return 23; diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 2c97f517bbc..c437848af7e 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -150,6 +150,9 @@ import 'vs/workbench/contrib/files/browser/files.contribution'; // Backup import 'vs/workbench/contrib/backup/common/backup.contribution'; +// bulkEdit +import 'vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution'; + // Search import 'vs/workbench/contrib/search/browser/search.contribution'; import 'vs/workbench/contrib/search/browser/searchView'; From d0012dd3329fcb9dfa341a1e8d7da397fced475b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 18 Dec 2019 12:27:52 +0100 Subject: [PATCH 014/843] prepare workspace edit a little before previewing --- .../bulkEdit/browser/bulkEditService.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 40739d7055a..7ad6c316423 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -404,8 +404,31 @@ export class BulkEditService implements IBulkEditService { }); } + private static _mergeSequentialTextEditsOfSameResource(workspaceEdit: WorkspaceEdit): WorkspaceEdit { + let newEdit: WorkspaceEdit = { edits: [] }; + let last: ResourceTextEdit | undefined; + for (let edit of workspaceEdit.edits) { + if (!isResourceTextEdit(edit)) { + last = undefined; + newEdit.edits.push(edit); + + } else { + if (!last || last.resource.toString() !== edit.resource.toString()) { + last = edit; + newEdit.edits.push(last); + } else { + last.edits.push(...edit.edits); + last.modelVersionId = last.modelVersionId || edit.modelVersionId; + } + } + } + return newEdit; + } + async apply(edit: WorkspaceEdit, options: IBulkEditOptions = {}): Promise { + edit = BulkEditService._mergeSequentialTextEditsOfSameResource(edit); + if (this._previewHandler) { edit = await this._previewHandler(edit, options); } From bd28239959546eaa83c1e72fb3dcb14edb2001e2 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 18 Dec 2019 16:05:17 +0100 Subject: [PATCH 015/843] token based line preview --- src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 372366e9b0f..c613be3fcab 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -65,12 +65,18 @@ export class BulkEditDataSource implements IAsyncDataSource { const range = Range.lift(edit.range); + const tokens = textModel.getLineTokens(range.endLineNumber); + let suffixLen = 0; + for (let idx = tokens.findTokenIndexAtOffset(range.endColumn); suffixLen < 50 && idx < tokens.getCount(); idx++) { + suffixLen += tokens.getEndOffset(idx) - tokens.getStartOffset(idx); + } + return new TextEditElement( edit, textModel.getValueInRange(new Range(range.startLineNumber, 1, range.startLineNumber, range.startColumn)), // line start to edit start, textModel.getValueInRange(range), edit.text, - textModel.getValueInRange(new Range(range.endLineNumber, range.endColumn, range.endLineNumber, range.endColumn + 20)) + textModel.getValueInRange(new Range(range.endLineNumber, range.endColumn, range.endLineNumber, range.endColumn + suffixLen)) ); }); From b1e6285a52f2266c928e91415d59191ebad2dfde Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 18 Dec 2019 16:11:58 +0100 Subject: [PATCH 016/843] expand first node --- .../workbench/contrib/bulkEdit/browser/bulkEditPanel.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts index fa7add19ca6..1333815aebc 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts @@ -8,7 +8,7 @@ import { Panel } from 'vs/workbench/browser/panel'; import { Dimension, addClass } from 'vs/base/browser/dom'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; -import { Edit, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; +import { Edit, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -76,10 +76,16 @@ export class BulkEditPanel extends Panel { this._discardAction.enabled = true; return new Promise(async resolve => { + this._currentResolve = resolve; await this._tree.setInput(edit); this._tree.domFocus(); this._tree.focusFirst(); + + const first = this._tree.getFirstElementChild(); + if (first instanceof FileElement) { + this._tree.expand(first); + } }); } From 5a6e8e89362f5e43751494e095c46144e81bdefc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 18 Dec 2019 16:30:09 +0100 Subject: [PATCH 017/843] show a default/empty message --- .../contrib/bulkEdit/browser/bulkEdit.css | 16 +++++++++ .../contrib/bulkEdit/browser/bulkEditPanel.ts | 35 ++++++++++++++++--- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css index c46eb7bd817..14043b9d484 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css @@ -7,4 +7,20 @@ text-decoration: line-through; } +.monaco-workbench .bulk-edit-panel .message { + padding: 10px 20px +} + +.monaco-workbench .bulk-edit-panel[data-state="message"] .message, +.monaco-workbench .bulk-edit-panel[data-state="data"] .tree +{ + display: inherit; +} + +.monaco-workbench .bulk-edit-panel[data-state="data"] .message, +.monaco-workbench .bulk-edit-panel[data-state="message"] .tree +{ + display: none; +} + diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts index 1333815aebc..c1f448a8d17 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts @@ -5,7 +5,7 @@ import 'vs/css!./bulkEdit'; import { Panel } from 'vs/workbench/browser/panel'; -import { Dimension, addClass } from 'vs/base/browser/dom'; +import { Dimension } from 'vs/base/browser/dom'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; import { Edit, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; @@ -16,6 +16,12 @@ import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } import { IStorageService } from 'vs/platform/storage/common/storage'; import { Action, IAction } from 'vs/base/common/actions'; import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistry'; +import { localize } from 'vs/nls'; + +const enum State { + Data = 'data', + Message = 'message' +} export class BulkEditPanel extends Panel { @@ -23,8 +29,10 @@ export class BulkEditPanel extends Panel { private static EmptyWorkspaceEdit = { edits: [] }; private _tree!: WorkbenchAsyncDataTree; - private _acceptAction: IAction; - private _discardAction: IAction; + private _message!: HTMLSpanElement; + + private readonly _acceptAction: IAction; + private readonly _discardAction: IAction; constructor( @IInstantiationService private readonly _instaService: IInstantiationService, @@ -40,10 +48,11 @@ export class BulkEditPanel extends Panel { create(parent: HTMLElement): void { super.create(parent); + parent.className = 'bulk-edit-panel'; - addClass(parent, 'bulk-edit-panel'); - + // tree const treeContainer = document.createElement('div'); + treeContainer.className = 'tree'; treeContainer.style.width = '100%'; treeContainer.style.height = '100%'; parent.appendChild(treeContainer); @@ -57,15 +66,29 @@ export class BulkEditPanel extends Panel { identityProvider: new BulkEditIdentityProvider() } ); + + // tree + this._message = document.createElement('span'); + this._message.className = 'message'; + this._message.innerText = localize('empty.msg', "Invoke a code action, like rename, to see a preview of its changes here."); + parent.appendChild(this._message); + + // + this._setState(State.Message); } layout(dimension: Dimension): void { this._tree.layout(dimension.height, dimension.width); } + private _setState(state: State): void { + this.getContainer()!.dataset['state'] = state; + } + private _currentResolve?: (apply: boolean) => void; setInput(edit: WorkspaceEdit): Promise { + this._setState(State.Data); if (this._currentResolve) { this._currentResolve(false); @@ -90,6 +113,8 @@ export class BulkEditPanel extends Panel { } private _done(accept: boolean): void { + this._setState(State.Message); + if (this._currentResolve) { this._currentResolve(accept); this._acceptAction.enabled = false; From b38926424a28270476b1d7037818c66c0b6359e7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 18 Dec 2019 16:34:43 +0100 Subject: [PATCH 018/843] nls --- src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts index c1f448a8d17..bf2784facd8 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts @@ -42,8 +42,8 @@ export class BulkEditPanel extends Panel { ) { super(BulkEditPanel.ID, telemetryService, themeService, storageService); - this._acceptAction = new Action('ok', 'Apply', 'codicon-check', false, async () => this._done(true)); - this._discardAction = new Action('ok', 'Discard', 'codicon-trash', false, async () => this._done(false)); + this._acceptAction = new Action('ok', localize('ok', "Apply Refactoring"), 'codicon-check', false, async () => this._done(true)); + this._discardAction = new Action('discard', localize('discard', "Discard"), 'codicon-trash', false, async () => this._done(false)); } create(parent: HTMLElement): void { From a8e2d7ce83aa2e7450602d19b9e32358e70a7abb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 18 Dec 2019 18:10:33 +0100 Subject: [PATCH 019/843] show preview diff editor --- .../bulkEdit/browser/bulkEdit.contribution.ts | 6 ++ .../contrib/bulkEdit/browser/bulkEditPanel.ts | 69 +++++++++++--- .../bulkEdit/browser/bulkEditPreview.ts | 91 +++++++++++-------- .../contrib/bulkEdit/browser/bulkEditTree.ts | 8 +- 4 files changed, 124 insertions(+), 50 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index 1d6f1930587..3116faa32bf 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -35,6 +35,12 @@ class BulkEditPreviewContribution { if (!apply) { return { edits: [] }; } + + // todo@joh/steVen add view state + // if (this._panelService.getLastActivePanelId() === BulkEditPanel.ID) { + // this._panelService.hideActivePanel(); + // } + // todo@joh get 'real' edit return edit; } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts index bf2784facd8..9b93404213a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts @@ -8,15 +8,19 @@ import { Panel } from 'vs/workbench/browser/panel'; import { Dimension } from 'vs/base/browser/dom'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; -import { Edit, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; +import { Edit, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { Action, IAction } from 'vs/base/common/actions'; +import { Action } from 'vs/base/common/actions'; import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { BulkEditPreviewProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { ILabelService } from 'vs/platform/label/common/label'; const enum State { Data = 'data', @@ -31,19 +35,27 @@ export class BulkEditPanel extends Panel { private _tree!: WorkbenchAsyncDataTree; private _message!: HTMLSpanElement; - private readonly _acceptAction: IAction; - private readonly _discardAction: IAction; + private readonly _acceptAction = new Action('ok', localize('ok', "Apply Refactoring"), 'codicon-check', false, async () => this._done(true)); + private readonly _discardAction = new Action('discard', localize('discard', "Discard"), 'codicon-trash', false, async () => this._done(false)); + private readonly _disposables = new DisposableStore(); + + private readonly _sessionDisposables = new DisposableStore(); + private _currentResolve?: (apply: boolean) => void; constructor( @IInstantiationService private readonly _instaService: IInstantiationService, + @IEditorService private readonly _editorService: IEditorService, + @ILabelService private readonly _labelService: ILabelService, @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, - @IStorageService storageService: IStorageService + @IStorageService storageService: IStorageService, ) { super(BulkEditPanel.ID, telemetryService, themeService, storageService); + } - this._acceptAction = new Action('ok', localize('ok', "Apply Refactoring"), 'codicon-check', false, async () => this._done(true)); - this._discardAction = new Action('discard', localize('discard', "Discard"), 'codicon-trash', false, async () => this._done(false)); + dispose(): void { + this._tree.dispose(); + this._disposables.dispose(); } create(parent: HTMLElement): void { @@ -67,7 +79,16 @@ export class BulkEditPanel extends Panel { } ); - // tree + this._disposables.add(this._tree.onDidOpen(e => { + const [first] = e.elements; + if (first instanceof TextEditElement) { + this._previewTextEditElement(first); + } else if (first instanceof FileElement) { + this._previewFileElement(first); + } + })); + + // message this._message = document.createElement('span'); this._message.className = 'message'; this._message.innerText = localize('empty.msg', "Invoke a code action, like rename, to see a preview of its changes here."); @@ -77,6 +98,10 @@ export class BulkEditPanel extends Panel { this._setState(State.Message); } + getActions() { + return [this._acceptAction, this._discardAction]; + } + layout(dimension: Dimension): void { this._tree.layout(dimension.height, dimension.width); } @@ -85,10 +110,11 @@ export class BulkEditPanel extends Panel { this.getContainer()!.dataset['state'] = state; } - private _currentResolve?: (apply: boolean) => void; - setInput(edit: WorkspaceEdit): Promise { this._setState(State.Data); + this._sessionDisposables.clear(); + + this._sessionDisposables.add(this._instaService.createInstance(BulkEditPreviewProvider, edit)); if (this._currentResolve) { this._currentResolve(false); @@ -114,7 +140,7 @@ export class BulkEditPanel extends Panel { private _done(accept: boolean): void { this._setState(State.Message); - + this._sessionDisposables.clear(); if (this._currentResolve) { this._currentResolve(accept); this._acceptAction.enabled = false; @@ -123,8 +149,25 @@ export class BulkEditPanel extends Panel { } } - getActions() { - return [this._acceptAction, this._discardAction]; + private async _previewTextEditElement(edit: TextEditElement): Promise { + + const previewUri = BulkEditPreviewProvider.asPreviewUri(edit.parent.resource); + + this._editorService.openEditor({ + leftResource: edit.parent.resource, + rightResource: previewUri, + label: localize('edt.title', "{0} (Refactor Preview)", this._labelService.getUriLabel(edit.parent.resource)), + options: { + selection: edit.edit.range + // preserveFocus, + // pinned, + // revealIfVisible: true + } + }); + } + + private _previewFileElement(edit: FileElement): void { + } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index c5fdb4f4ce1..df7e8edb345 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -3,50 +3,69 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree'; -// import * as modes from 'vs/editor/common/modes'; -// import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; -// import { URI } from 'vs/base/common/uri'; -// import { ITextModel } from 'vs/editor/common/model'; -// import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -// import { IModeService } from 'vs/editor/common/services/modeService'; -// import { IModelService } from 'vs/editor/common/services/modelService'; -// import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; +import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; +import { URI } from 'vs/base/common/uri'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; +import { WorkspaceEdit, isResourceTextEdit, TextEdit } from 'vs/editor/common/modes'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { flatten, mergeSort } from 'vs/base/common/arrays'; +import { Range } from 'vs/editor/common/core/range'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; -// export class BulkEditPreviewProvider implements ITextModelContentProvider { +export class BulkEditPreviewProvider implements ITextModelContentProvider { -// static readonly Schema = 'vscode-bulkeditpreview'; + static readonly Schema = 'vscode-bulkeditpreview'; -// static asPreviewUri(uri: URI): URI { -// return URI.from({ scheme: BulkEditPreviewProvider.Schema, path: uri.toString() }); -// } + static asPreviewUri(uri: URI): URI { + return URI.from({ scheme: BulkEditPreviewProvider.Schema, path: uri.toString() }); + } -// static fromPreviewUri(uri: URI): URI { -// return URI.parse(uri.path); -// } + static fromPreviewUri(uri: URI): URI { + return URI.parse(uri.path); + } -// constructor( -// @IModeService private readonly _modeService: IModeService, -// @IModelService private readonly _modelService: IModelService, -// @ITextModelService private readonly textModelResolverService: ITextModelService -// ) { -// this.textModelResolverService.registerTextModelContentProvider(BulkEditPreviewProvider.Schema, this); -// } + private readonly _reg: IDisposable; -// async provideTextContent(previewUri: URI) { + constructor( + private readonly _edit: WorkspaceEdit, + @IModeService private readonly _modeService: IModeService, + @IModelService private readonly _modelService: IModelService, + @ITextModelService private readonly textModelResolverService: ITextModelService + ) { + this._reg = this.textModelResolverService.registerTextModelContentProvider(BulkEditPreviewProvider.Schema, this); + } -// const resourceUri = BulkEditPreviewProvider.fromPreviewUri(previewUri); + dispose(): void { + this._reg.dispose(); + } -// const ref = await this.textModelResolverService.createModelReference(resourceUri); + async provideTextContent(previewUri: URI) { -// const sourceModel = ref.object.textEditorModel; + const resourceUri = BulkEditPreviewProvider.fromPreviewUri(previewUri); -// const previewModel = this._modelService.createModel( -// createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), -// this._modeService.create(sourceModel.getLanguageIdentifier().language), -// previewUri -// ); + const ref = await this.textModelResolverService.createModelReference(resourceUri); -// return null; -// } -// } + const sourceModel = ref.object.textEditorModel; + + const previewModel = this._modelService.createModel( + createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), + this._modeService.create(sourceModel.getLanguageIdentifier().language), + previewUri + ); + + const textEdits: TextEdit[][] = []; + for (let edit of this._edit.edits) { + if (isResourceTextEdit(edit) && edit.resource.toString() === resourceUri.toString()) { + textEdits.push(edit.edits); + } + } + + let allEdits = flatten(textEdits).map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text)); + allEdits = mergeSort(allEdits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + previewModel.applyEdits(allEdits); + + return previewModel; + } +} diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index c613be3fcab..ce48ebacc1c 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -29,7 +29,12 @@ export class FileElement { } export class TextEditElement { - constructor(readonly edit: modes.TextEdit, readonly prefix: string, readonly selecting: string, readonly inserting: string, readonly suffix: string) { } + + constructor( + readonly parent: modes.ResourceTextEdit, + readonly edit: modes.TextEdit, + readonly prefix: string, readonly selecting: string, readonly inserting: string, readonly suffix: string + ) { } } export type Edit = FileElement | TextEditElement; @@ -72,6 +77,7 @@ export class BulkEditDataSource implements IAsyncDataSourceelement.edit, edit, textModel.getValueInRange(new Range(range.startLineNumber, 1, range.startLineNumber, range.startColumn)), // line start to edit start, textModel.getValueInRange(range), From 2385505c9d99d5e3a8ee557a9ba2765c856791b0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 20 Dec 2019 16:19:36 +0100 Subject: [PATCH 020/843] release references --- src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts | 2 +- src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index df7e8edb345..bd30a397985 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -65,7 +65,7 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider { let allEdits = flatten(textEdits).map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text)); allEdits = mergeSort(allEdits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)); previewModel.applyEdits(allEdits); - + ref.dispose(); return previewModel; } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index ce48ebacc1c..02711a2c196 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -85,7 +85,7 @@ export class BulkEditDataSource implements IAsyncDataSource Date: Fri, 20 Dec 2019 16:45:16 +0100 Subject: [PATCH 021/843] add noPreview options --- src/vs/editor/browser/services/bulkEditService.ts | 1 + src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index a21f7252590..7c433166856 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -14,6 +14,7 @@ export const IBulkEditService = createDecorator('IWorkspaceEdi export interface IBulkEditOptions { editor?: ICodeEditor; progress?: IProgress; + noPreview?: boolean; } export interface IBulkEditResult { diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 7ad6c316423..08390b52432 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -429,7 +429,7 @@ export class BulkEditService implements IBulkEditService { edit = BulkEditService._mergeSequentialTextEditsOfSameResource(edit); - if (this._previewHandler) { + if (this._previewHandler && !options.noPreview) { edit = await this._previewHandler(edit, options); } From 34fcef5cdd6eebb6e2951f62374b6a09ae235a76 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 23 Dec 2019 14:34:34 +0100 Subject: [PATCH 022/843] implement preview file system... --- .../bulkEdit/browser/bulkEditPreview.ts | 222 +++++++++++++++++- 1 file changed, 221 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index bd30a397985..b8035d4478a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -9,10 +9,14 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { WorkspaceEdit, isResourceTextEdit, TextEdit } from 'vs/editor/common/modes'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { flatten, mergeSort } from 'vs/base/common/arrays'; import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; +import * as files from 'vs/platform/files/common/files'; +import { Event, Emitter } from 'vs/base/common/event'; +import { TernarySearchTree } from 'vs/base/common/map'; +import { basename } from 'vs/base/common/resources'; export class BulkEditPreviewProvider implements ITextModelContentProvider { @@ -69,3 +73,219 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider { return previewModel; } } + + +export function asPreviewUri(uri: URI): URI { + return URI.from({ scheme: 'vscode-bulkedit-preview', path: uri.path, query: uri.toString() }); +} + +export function fromPreviewUri(uri: URI): URI { + return URI.parse(uri.query); +} + +export function asPreviewEdit(edit: WorkspaceEdit): WorkspaceEdit { + const result: WorkspaceEdit = { edits: [] }; + for (let child of edit.edits) { + if (isResourceTextEdit(child)) { + result.edits.push({ ...child, resource: asPreviewUri(child.resource) }); + } else { + result.edits.push({ + oldUri: child.oldUri && asPreviewUri(child.oldUri), + newUri: child.newUri && asPreviewUri(child.newUri), + options: child.options + }); + } + } + return result; +} + +class File implements files.IStat { + + type: files.FileType; + ctime: number; + mtime: number; + size: number; + + name: string; + data?: Uint8Array; + + constructor(name: string) { + this.type = files.FileType.File; + this.ctime = Date.now(); + this.mtime = Date.now(); + this.size = 0; + this.name = name; + } +} + +class Directory implements files.IStat { + + type: files.FileType; + ctime: number; + mtime: number; + size: number; + + name: string; + entries: Map; + + constructor(name: string) { + this.type = files.FileType.Directory; + this.ctime = Date.now(); + this.mtime = Date.now(); + this.size = 0; + this.name = name; + this.entries = new Map(); + } +} + +type Entry = File | Directory; + +export class BulkEditFileSystem implements files.IFileSystemProvider { + + readonly capabilities = files.FileSystemProviderCapabilities.FileReadWrite; + readonly onDidChangeCapabilities = Event.None; + + private readonly _registration: IDisposable; + private readonly _onDidChangeFile = new Emitter(); + readonly onDidChangeFile = this._onDidChangeFile.event; + + private readonly _entries = TernarySearchTree.forPaths(); + private readonly _deleted = TernarySearchTree.forPaths(); + + private _pendingChanges: files.IFileChange[] = []; + private _pendingHandle?: any; + + constructor(@files.IFileService private _fileService: files.IFileService) { + this._registration = _fileService.registerProvider('vscode-bulkedit-preview', this); + } + + dispose(): void { + this._registration.dispose(); + } + + private _fireSoon(event: files.IFileChange): void { + this._pendingChanges.push(event); + clearTimeout(this._pendingHandle); + this._pendingHandle = setTimeout(() => { + this._onDidChangeFile.fire(this._pendingChanges.slice(0)); + this._pendingChanges = []; + }, 0); + } + + private _checkDeleted(resource: URI): void { + if (this._deleted.findSubstr(resource.toString())) { + throw new Error('deleted ' + resource.toString()); + } + } + + watch(_resource: URI, _opts: files.IWatchOptions): IDisposable { + return Disposable.None; + } + + async stat(resource: URI): Promise { + this._checkDeleted(resource); + const entry = this._entries.get(resource.toString()); + if (entry) { + return entry; + } + + const stat = await this._fileService.resolve(fromPreviewUri(resource), { resolveMetadata: true }); + return { + type: (stat.isSymbolicLink ? files.FileType.SymbolicLink : 0) + (stat.isFile ? files.FileType.File : 0) + (stat.isDirectory ? files.FileType.Directory : 0), + ctime: stat.ctime, + mtime: stat.mtime, + size: stat.size, + }; + } + + async readdir(resource: URI): Promise<[string, files.FileType][]> { + // resource = fromPreviewUri(resource); + this._checkDeleted(resource); + + const entry = this._entries.get(resource.toString()); + const result: [string, files.FileType][] = []; + if (entry instanceof Directory) { + entry.entries.forEach(value => result.push([value.name, value.type])); + } + try { + const stat = await this._fileService.resolve(fromPreviewUri(resource), { resolveMetadata: true }); + if (stat.children) { + for (let child of stat.children) { + result.push([ + child.name, + (child.isSymbolicLink ? files.FileType.SymbolicLink : 0) + (child.isFile ? files.FileType.File : 0) + (child.isDirectory ? files.FileType.Directory : 0) + ]); + } + } + + + } catch { + // ignore + } + + return result; + } + + async mkdir(resource: URI): Promise { + // resource = fromPreviewUri(resource); + + const dir = new Directory(basename(resource)); + this._entries.set(resource.toString(), dir); + this._fireSoon({ resource, type: files.FileChangeType.ADDED }); + } + + async delete(resource: URI, _opts: files.FileDeleteOptions): Promise { + // resource = fromPreviewUri(resource); + this._deleted.set(resource.toString(), true); + } + + async rename(from: URI, to: URI, opts: files.FileOverwriteOptions): Promise { + // from = fromPreviewUri(from); + // to = fromPreviewUri(to); + + const target = new File(basename(to)); + target.type = (await this.stat(from)).type; + + this._deleted.set(from.toString(), true); + this._entries.set(to.toString(), target); + + // todo@joh copy files + // const iter = this._entries.findSuperstr(from.toString()); + // if (iter) { + // for (let next = iter.next(); !next.done; next = iter.next()) { + + // } + // } + // this._entries.delete(from.toString()); + + //todo@joh RENAME EVENT? + this._fireSoon({ resource: from, type: files.FileChangeType.DELETED }); + this._fireSoon({ resource: to, type: files.FileChangeType.ADDED }); + } + + // copy?(from: URI, to: URI, opts: FileOverwriteOptions): Promise; + + async readFile(resource: URI): Promise { + this._checkDeleted(resource); + + const entry = this._entries.get(resource.toString()); + if (entry instanceof File) { + return entry.data || new Uint8Array(); + } + return (await this._fileService.readFile(fromPreviewUri(resource))).value.buffer; + } + + async writeFile(resource: URI, content: Uint8Array, opts: files.FileWriteOptions): Promise { + + let entry = this._entries.get(resource.toString()); + if (!entry && opts.create) { + entry = new File(basename(resource)); + this._entries.set(resource.toString(), entry); + } + if (!(entry instanceof File)) { + throw new Error(); + } + entry.data = content; + this._fireSoon({ resource, type: files.FileChangeType.UPDATED }); + } +} From 8add944b5435f36b962796449ac8c556fdc32a6c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 23 Dec 2019 14:40:20 +0100 Subject: [PATCH 023/843] use preview fs --- .../contrib/bulkEdit/browser/bulkEditPanel.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts index 9b93404213a..a312655cf4a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts @@ -19,8 +19,9 @@ import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistr import { localize } from 'vs/nls'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { BulkEditPreviewProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { BulkEditFileSystem, asPreviewEdit, fromPreviewUri } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; import { ILabelService } from 'vs/platform/label/common/label'; +import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; const enum State { Data = 'data', @@ -44,6 +45,7 @@ export class BulkEditPanel extends Panel { constructor( @IInstantiationService private readonly _instaService: IInstantiationService, + @IBulkEditService private readonly _bulkEditService: IBulkEditService, @IEditorService private readonly _editorService: IEditorService, @ILabelService private readonly _labelService: ILabelService, @ITelemetryService telemetryService: ITelemetryService, @@ -110,11 +112,17 @@ export class BulkEditPanel extends Panel { this.getContainer()!.dataset['state'] = state; } - setInput(edit: WorkspaceEdit): Promise { + async setInput(edit: WorkspaceEdit): Promise { this._setState(State.Data); this._sessionDisposables.clear(); - this._sessionDisposables.add(this._instaService.createInstance(BulkEditPreviewProvider, edit)); + this._sessionDisposables.add(this._instaService.createInstance(BulkEditFileSystem)); + + const previewEdit = asPreviewEdit(edit); + + const summary = await this._bulkEditService.apply(previewEdit, { noPreview: true }); + console.log(summary); + // this._sessionDisposables.add(this._instaService.createInstance(BulkEditPreviewProvider, edit)); if (this._currentResolve) { this._currentResolve(false); @@ -127,7 +135,7 @@ export class BulkEditPanel extends Panel { return new Promise(async resolve => { this._currentResolve = resolve; - await this._tree.setInput(edit); + await this._tree.setInput(previewEdit); this._tree.domFocus(); this._tree.focusFirst(); @@ -151,11 +159,11 @@ export class BulkEditPanel extends Panel { private async _previewTextEditElement(edit: TextEditElement): Promise { - const previewUri = BulkEditPreviewProvider.asPreviewUri(edit.parent.resource); + // const previewUri = BulkEditPreviewProvider.asPreviewUri(edit.parent.resource); this._editorService.openEditor({ - leftResource: edit.parent.resource, - rightResource: previewUri, + leftResource: fromPreviewUri(edit.parent.resource), + rightResource: edit.parent.resource, label: localize('edt.title', "{0} (Refactor Preview)", this._labelService.getUriLabel(edit.parent.resource)), options: { selection: edit.edit.range From 6e06517057645121558accc1a3931a1e7a5f8360 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 23 Dec 2019 15:00:13 +0100 Subject: [PATCH 024/843] Revert "use preview fs" This reverts commit 8add944b5435f36b962796449ac8c556fdc32a6c. --- .../contrib/bulkEdit/browser/bulkEditPanel.ts | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts index a312655cf4a..9b93404213a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts @@ -19,9 +19,8 @@ import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistr import { localize } from 'vs/nls'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { BulkEditFileSystem, asPreviewEdit, fromPreviewUri } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { BulkEditPreviewProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; import { ILabelService } from 'vs/platform/label/common/label'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; const enum State { Data = 'data', @@ -45,7 +44,6 @@ export class BulkEditPanel extends Panel { constructor( @IInstantiationService private readonly _instaService: IInstantiationService, - @IBulkEditService private readonly _bulkEditService: IBulkEditService, @IEditorService private readonly _editorService: IEditorService, @ILabelService private readonly _labelService: ILabelService, @ITelemetryService telemetryService: ITelemetryService, @@ -112,17 +110,11 @@ export class BulkEditPanel extends Panel { this.getContainer()!.dataset['state'] = state; } - async setInput(edit: WorkspaceEdit): Promise { + setInput(edit: WorkspaceEdit): Promise { this._setState(State.Data); this._sessionDisposables.clear(); - this._sessionDisposables.add(this._instaService.createInstance(BulkEditFileSystem)); - - const previewEdit = asPreviewEdit(edit); - - const summary = await this._bulkEditService.apply(previewEdit, { noPreview: true }); - console.log(summary); - // this._sessionDisposables.add(this._instaService.createInstance(BulkEditPreviewProvider, edit)); + this._sessionDisposables.add(this._instaService.createInstance(BulkEditPreviewProvider, edit)); if (this._currentResolve) { this._currentResolve(false); @@ -135,7 +127,7 @@ export class BulkEditPanel extends Panel { return new Promise(async resolve => { this._currentResolve = resolve; - await this._tree.setInput(previewEdit); + await this._tree.setInput(edit); this._tree.domFocus(); this._tree.focusFirst(); @@ -159,11 +151,11 @@ export class BulkEditPanel extends Panel { private async _previewTextEditElement(edit: TextEditElement): Promise { - // const previewUri = BulkEditPreviewProvider.asPreviewUri(edit.parent.resource); + const previewUri = BulkEditPreviewProvider.asPreviewUri(edit.parent.resource); this._editorService.openEditor({ - leftResource: fromPreviewUri(edit.parent.resource), - rightResource: edit.parent.resource, + leftResource: edit.parent.resource, + rightResource: previewUri, label: localize('edt.title', "{0} (Refactor Preview)", this._labelService.getUriLabel(edit.parent.resource)), options: { selection: edit.edit.range From ea9c2f030bbb34df17015544908d84e9bbf98ac7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 23 Dec 2019 16:09:38 +0100 Subject: [PATCH 025/843] follow text content provider approach --- .../contrib/bulkEdit/browser/bulkEditPanel.ts | 14 ++- .../bulkEdit/browser/bulkEditPreview.ts | 99 ++++++++++++------- .../contrib/bulkEdit/browser/bulkEditTree.ts | 19 +++- 3 files changed, 95 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts index 9b93404213a..aab4b172409 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts @@ -21,6 +21,8 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { BulkEditPreviewProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; import { ILabelService } from 'vs/platform/label/common/label'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { URI } from 'vs/base/common/uri'; const enum State { Data = 'data', @@ -46,6 +48,7 @@ export class BulkEditPanel extends Panel { @IInstantiationService private readonly _instaService: IInstantiationService, @IEditorService private readonly _editorService: IEditorService, @ILabelService private readonly _labelService: ILabelService, + @ITextModelService private readonly _textModelService: ITextModelService, @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @IStorageService storageService: IStorageService, @@ -151,10 +154,19 @@ export class BulkEditPanel extends Panel { private async _previewTextEditElement(edit: TextEditElement): Promise { + let leftResource: URI; + + try { + (await this._textModelService.createModelReference(edit.parent.resource)).dispose(); + leftResource = edit.parent.resource; + } catch { + leftResource = BulkEditPreviewProvider.emptyPreview; + } + const previewUri = BulkEditPreviewProvider.asPreviewUri(edit.parent.resource); this._editorService.openEditor({ - leftResource: edit.parent.resource, + leftResource, rightResource: previewUri, label: localize('edt.title', "{0} (Refactor Preview)", this._labelService.getUriLabel(edit.parent.resource)), options: { diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index b8035d4478a..59695cdc2ec 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -8,9 +8,9 @@ import { URI } from 'vs/base/common/uri'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; -import { WorkspaceEdit, isResourceTextEdit, TextEdit } from 'vs/editor/common/modes'; -import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { flatten, mergeSort } from 'vs/base/common/arrays'; +import { WorkspaceEdit, isResourceTextEdit, isResourceFileEdit } from 'vs/editor/common/modes'; +import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { mergeSort } from 'vs/base/common/arrays'; import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import * as files from 'vs/platform/files/common/files'; @@ -22,55 +22,88 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider { static readonly Schema = 'vscode-bulkeditpreview'; + static emptyPreview = URI.from({ scheme: BulkEditPreviewProvider.Schema, fragment: 'empty' }); + static asPreviewUri(uri: URI): URI { - return URI.from({ scheme: BulkEditPreviewProvider.Schema, path: uri.toString() }); + return URI.from({ scheme: BulkEditPreviewProvider.Schema, path: uri.path, query: uri.toString() }); } static fromPreviewUri(uri: URI): URI { - return URI.parse(uri.path); + return URI.parse(uri.query); } - private readonly _reg: IDisposable; + private readonly _disposables = new DisposableStore(); + private readonly _ready: Promise; constructor( private readonly _edit: WorkspaceEdit, @IModeService private readonly _modeService: IModeService, @IModelService private readonly _modelService: IModelService, - @ITextModelService private readonly textModelResolverService: ITextModelService + @ITextModelService private readonly _textModelResolverService: ITextModelService ) { - this._reg = this.textModelResolverService.registerTextModelContentProvider(BulkEditPreviewProvider.Schema, this); + this._disposables.add(this._textModelResolverService.registerTextModelContentProvider(BulkEditPreviewProvider.Schema, this)); + this._ready = this._prepareModels(); } dispose(): void { - this._reg.dispose(); + this._disposables.dispose(); + } + + private async _prepareModels() { + + const getOrCreatePreviewModel = async (uri: URI) => { + const previewUri = BulkEditPreviewProvider.asPreviewUri(uri); + let model = this._modelService.getModel(previewUri); + if (!model) { + try { + // try: copy existing + const ref = await this._textModelResolverService.createModelReference(uri); + const sourceModel = ref.object.textEditorModel; + model = this._modelService.createModel( + createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), + this._modeService.create(sourceModel.getLanguageIdentifier().language), + previewUri + ); + ref.dispose(); + + } catch { + // create NEW model + model = this._modelService.createModel( + '', + this._modeService.createByFilepathOrFirstLine(previewUri), + previewUri + ); + } + this._disposables.add(model); + } + return model; + }; + + for (let edit of this._edit.edits) { + if (isResourceFileEdit(edit)) { + if (URI.isUri(edit.newUri)) { + await getOrCreatePreviewModel(edit.newUri); + } + if (URI.isUri(edit.oldUri)) { + await getOrCreatePreviewModel(edit.oldUri); + } + } else { + const model = await getOrCreatePreviewModel(edit.resource); + const editOperations = mergeSort( + edit.edits.map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text)), + (a, b) => Range.compareRangesUsingStarts(a.range, b.range) + ); + model.applyEdits(editOperations); + } + } } async provideTextContent(previewUri: URI) { - - const resourceUri = BulkEditPreviewProvider.fromPreviewUri(previewUri); - - const ref = await this.textModelResolverService.createModelReference(resourceUri); - - const sourceModel = ref.object.textEditorModel; - - const previewModel = this._modelService.createModel( - createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), - this._modeService.create(sourceModel.getLanguageIdentifier().language), - previewUri - ); - - const textEdits: TextEdit[][] = []; - for (let edit of this._edit.edits) { - if (isResourceTextEdit(edit) && edit.resource.toString() === resourceUri.toString()) { - textEdits.push(edit.edits); - } + if (previewUri.toString() === BulkEditPreviewProvider.emptyPreview.toString()) { + return this._modelService.createModel('', null, previewUri); } - - let allEdits = flatten(textEdits).map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text)); - allEdits = mergeSort(allEdits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)); - previewModel.applyEdits(allEdits); - ref.dispose(); - return previewModel; + await this._ready; + return this._modelService.getModel(previewUri); } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 02711a2c196..7fee573a6dc 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -14,6 +14,9 @@ import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabe import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { Range } from 'vs/editor/common/core/range'; import * as dom from 'vs/base/browser/dom'; +import { ITextModel } from 'vs/editor/common/model'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { TextModel } from 'vs/editor/common/model/textModel'; // --- VIEW MODEL @@ -64,8 +67,17 @@ export class BulkEditDataSource implements IAsyncDataSource { const range = Range.lift(edit.range); @@ -85,7 +97,8 @@ export class BulkEditDataSource implements IAsyncDataSource Date: Sat, 28 Dec 2019 16:52:15 -0500 Subject: [PATCH 026/843] Fixes #79857 --- src/vs/base/browser/dom.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index b7a55a4ca64..40c7153ced7 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -287,7 +287,7 @@ export function addDisposableGenericMouseUpListner(node: EventTarget, handler: ( export function addDisposableNonBubblingMouseOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable { return addDisposableListener(node, 'mouseout', (e: MouseEvent) => { // Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements - let toElement: Node | null = (e.relatedTarget || e.target); + let toElement: Node | null = (e.relatedTarget); while (toElement && toElement !== node) { toElement = toElement.parentNode; } @@ -302,7 +302,7 @@ export function addDisposableNonBubblingMouseOutListener(node: Element, handler: export function addDisposableNonBubblingPointerOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable { return addDisposableListener(node, 'pointerout', (e: MouseEvent) => { // Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements - let toElement: Node | null = (e.relatedTarget || e.target); + let toElement: Node | null = (e.relatedTarget); while (toElement && toElement !== node) { toElement = toElement.parentNode; } From 37edc0377b021653905a255b9d9e4bcc113aef13 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 30 Dec 2019 14:59:20 +0100 Subject: [PATCH 027/843] editor: introduce setAria api --- src/vs/editor/browser/controller/textAreaHandler.ts | 13 +++++++++++++ src/vs/editor/browser/editorBrowser.ts | 13 +++++++++++++ src/vs/editor/browser/view/viewImpl.ts | 7 +++++-- src/vs/editor/browser/widget/codeEditorWidget.ts | 7 +++++++ src/vs/monaco.d.ts | 7 +++++++ 5 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 49ed53c81df..eff63581e43 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -29,6 +29,7 @@ import { RenderingContext, RestrictedRenderingContext, HorizontalPosition } from import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import { IEditorAriaOptions } from 'vs/editor/browser/editorBrowser'; export interface ITextAreaHandlerHelper { visibleRangeForPositionRelativeToEditor(lineNumber: number, column: number): HorizontalPosition | null; @@ -424,6 +425,18 @@ export class TextAreaHandler extends ViewPart { return this._lastRenderPosition; } + public setAria(options: IEditorAriaOptions): void { + if (options.activeDescendent) { + this.textArea.setAttribute('aria-haspopup', 'true'); + this.textArea.setAttribute('aria-autocomplete', 'list'); + this.textArea.setAttribute('aria-activedescendant', options.activeDescendent); + } else { + this.textArea.setAttribute('aria-haspopup', 'false'); + this.textArea.setAttribute('aria-autocomplete', 'both'); + this.textArea.removeAttribute('aria-activedescendant'); + } + } + // --- end view API private _primaryCursorPosition: Position = new Position(1, 1); diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index c359f7ee54d..4906fa6196a 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -319,6 +319,13 @@ export interface IOverviewRuler { setLayout(position: OverviewRulerPosition): void; } +/** + * Editor aria options. + */ +export interface IEditorAriaOptions { + activeDescendent: string | undefined; +} + /** * A rich code editor. */ @@ -689,6 +696,12 @@ export interface ICodeEditor extends editorCommon.IEditor { */ setHiddenAreas(ranges: IRange[]): void; + /** + * Sets the editor aria options, primarily the active descendent. + * @internal + */ + setAria(options: IEditorAriaOptions): void; + /** * @internal */ diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 28e858ea7e1..908dce7abb5 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -11,7 +11,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler'; import { ITextAreaHandlerHelper, TextAreaHandler } from 'vs/editor/browser/controller/textAreaHandler'; -import { IContentWidget, IContentWidgetPosition, IOverlayWidget, IOverlayWidgetPosition, IMouseTarget, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; +import { IContentWidget, IContentWidgetPosition, IOverlayWidget, IOverlayWidgetPosition, IMouseTarget, IViewZoneChangeAccessor, IEditorAriaOptions } from 'vs/editor/browser/editorBrowser'; import { ICommandDelegate, ViewController } from 'vs/editor/browser/view/viewController'; import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; import { ContentViewOverlays, MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays'; @@ -510,6 +510,10 @@ export class View extends ViewEventHandler { this._textAreaHandler.refreshFocusState(); } + public setAria(options: IEditorAriaOptions): void { + this._textAreaHandler.setAria(options); + } + public addContentWidget(widgetData: IContentWidgetData): void { this.contentWidgets.addWidget(widgetData.widget); this.layoutContentWidget(widgetData); @@ -559,4 +563,3 @@ function safeInvokeNoArg(func: Function): any { onUnexpectedError(e); } } - diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 9c84e1a8062..d0257074276 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1311,6 +1311,13 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._modelData.view.render(true, forceRedraw); } + public setAria(options: editorBrowser.IEditorAriaOptions): void { + if (!this._modelData || !this._modelData.hasRealView) { + return; + } + this._modelData.view.setAria(options); + } + public applyFontInfo(target: HTMLElement): void { Configuration.applyFontInfoSlow(target, this._configuration.options.get(EditorOption.fontInfo)); } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 27cece82f20..a5510176fed 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3726,6 +3726,13 @@ declare namespace monaco.editor { readonly target: IMouseTarget | null; } + /** + * Editor aria options. + */ + export interface IEditorAriaOptions { + activeDescendent: string | undefined; + } + /** * A rich code editor. */ From 623adc75e511ca54a93f28f6ff3c9eb83f9fb587 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 30 Dec 2019 14:59:37 +0100 Subject: [PATCH 028/843] suggest: do not use aria alert, instead pass the active descendent to the editor --- .../editor/contrib/suggest/suggestWidget.ts | 60 +++++++------------ 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index b3cf4a333ac..781f20bb44c 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -22,7 +22,6 @@ import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { Context as SuggestContext, CompletionItem } from './suggest'; import { CompletionModel } from './completionModel'; -import { alert } from 'vs/base/browser/ui/aria/aria'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { attachListStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; @@ -89,6 +88,10 @@ function canExpandCompletionItem(item: CompletionItem | null) { return (suggestion.detail && suggestion.detail !== suggestion.label); } +function getAriaId(index: number): string { + return `suggest-aria-id:${index}`; +} + class Renderer implements IListRenderer { constructor( @@ -162,6 +165,7 @@ class Renderer implements IListRenderer const data = templateData; const suggestion = (element).completion; + data.root.id = getAriaId(_index); data.icon.className = 'icon ' + completionKindToCssClass(suggestion.kind); data.colorspan.style.backgroundColor = ''; @@ -250,7 +254,6 @@ class SuggestionDetails { private header: HTMLElement; private type: HTMLElement; private docs: HTMLElement; - private ariaLabel: string | null; private readonly disposables: DisposableStore; private renderDisposeable?: IDisposable; private borderWidth: number = 1; @@ -279,7 +282,6 @@ class SuggestionDetails { this.type = append(this.header, $('p.type')); this.docs = append(this.body, $('p.docs')); - this.ariaLabel = null; this.configureFont(); @@ -318,7 +320,6 @@ class SuggestionDetails { this.type.textContent = ''; this.docs.textContent = ''; addClass(this.el, 'no-docs'); - this.ariaLabel = null; return; } removeClass(this.el, 'no-docs'); @@ -358,15 +359,6 @@ class SuggestionDetails { this.body.scrollTop = 0; this.scrollbar.scanDomNode(); - - this.ariaLabel = strings.format( - '{0}{1}', - detail || '', - documentation ? (typeof documentation === 'string' ? documentation : documentation.value) : ''); - } - - getAriaLabel() { - return this.ariaLabel; } scrollDown(much = 8): void { @@ -521,7 +513,22 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate false }, - mouseSupport: false + mouseSupport: false, + accessibilityProvider: { + getAriaLabel: (item: CompletionItem) => { + if (this.expandDocsSettingFromStorage()) { + const { documentation, detail } = item.completion; + const docs = strings.format( + '{0}{1}', + detail || '', + documentation ? (typeof documentation === 'string' ? documentation : documentation.value) : ''); + + return nls.localize('ariaCurrenttSuggestionReadDetails', "Item {0}, docs: {1}", item.completion.label, docs); + } else { + return item.completion.label; + } + } + } }); this.toDispose.add(attachListStyler(this.list, themeService, { @@ -610,25 +617,6 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate Date: Tue, 31 Dec 2019 12:20:23 +0100 Subject: [PATCH 029/843] suggest widget: _index should be index since it is used --- src/vs/editor/contrib/suggest/suggestWidget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 781f20bb44c..5d8670136b2 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -161,11 +161,11 @@ class Renderer implements IListRenderer return data; } - renderElement(element: CompletionItem, _index: number, templateData: ISuggestionTemplateData): void { + renderElement(element: CompletionItem, index: number, templateData: ISuggestionTemplateData): void { const data = templateData; const suggestion = (element).completion; - data.root.id = getAriaId(_index); + data.root.id = getAriaId(index); data.icon.className = 'icon ' + completionKindToCssClass(suggestion.kind); data.colorspan.style.backgroundColor = ''; From 5fc8d383e13dfb5cf5463f8c2fe12ef8cfeb9f8c Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 31 Dec 2019 15:22:49 +0100 Subject: [PATCH 030/843] completionItem isResolved --- src/vs/editor/contrib/suggest/suggest.ts | 7 ++++--- src/vs/editor/contrib/suggest/suggestWidget.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index d28423e1602..218ff2c70b4 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -44,6 +44,7 @@ export class CompletionItem { // sorting, filtering score: FuzzyScore = FuzzyScore.Default; distance: number = 0; + isResolved: boolean = false; idx?: number; word?: string; @@ -74,14 +75,14 @@ export class CompletionItem { const { resolveCompletionItem } = provider; if (typeof resolveCompletionItem !== 'function') { this.resolve = () => Promise.resolve(); + this.isResolved = true; } else { let cached: Promise | undefined; this.resolve = (token) => { if (!cached) { - let isDone = false; cached = Promise.resolve(resolveCompletionItem.call(provider, model, position, completion, token)).then(value => { assign(completion, value); - isDone = true; + this.isResolved = true; }, err => { if (isPromiseCanceledError(err)) { // the IPC queue will reject the request with the @@ -90,7 +91,7 @@ export class CompletionItem { } }); token.onCancellationRequested(() => { - if (!isDone) { + if (!this.isResolved) { // cancellation after the request has been // dispatched -> reset cache cached = undefined; diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 5d8670136b2..8a0b22cdb52 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -516,7 +516,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate { - if (this.expandDocsSettingFromStorage()) { + if (item.isResolved && this.expandDocsSettingFromStorage()) { const { documentation, detail } = item.completion; const docs = strings.format( '{0}{1}', From 5ed421a823d77f3c600db9d7d22fe65f38b4121e Mon Sep 17 00:00:00 2001 From: Brett Cannon <54418+brettcannon@users.noreply.github.com> Date: Tue, 31 Dec 2019 14:40:40 -0800 Subject: [PATCH 031/843] Fix a grammatical mistake in a docstring --- src/vs/vscode.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 1e9ffcc6d9c..a8131f133d9 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -9998,7 +9998,7 @@ declare module 'vscode' { export function getExtension(extensionId: string): Extension | undefined; /** - * Get an extension its full identifier in the form of: `publisher.name`. + * Get an extension by its full identifier in the form of: `publisher.name`. * * @param extensionId An extension identifier. * @return An extension or `undefined`. From c1a3ca9db9afaa04045a027af0d1e3645404e6d4 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 2 Jan 2020 13:14:15 -0800 Subject: [PATCH 032/843] Update diff icons to use codicons --- .../linesDecorations/linesDecorations.ts | 2 +- .../editor/browser/widget/diffEditorWidget.ts | 6 +-- .../editor/browser/widget/inlineDiffMargin.ts | 2 +- .../browser/widget/media/addition-dark.svg | 3 -- .../browser/widget/media/addition-light.svg | 3 -- .../browser/widget/media/close-dark.svg | 3 -- .../browser/widget/media/close-light.svg | 3 -- .../browser/widget/media/deletion-dark.svg | 3 -- .../browser/widget/media/deletion-light.svg | 3 -- .../browser/widget/media/diffEditor.css | 39 ++----------------- .../browser/widget/media/diffReview.css | 7 ---- .../browser/widget/media/lightbulb-dark.svg | 3 -- .../browser/widget/media/lightbulb-light.svg | 4 -- 13 files changed, 9 insertions(+), 72 deletions(-) delete mode 100644 src/vs/editor/browser/widget/media/addition-dark.svg delete mode 100644 src/vs/editor/browser/widget/media/addition-light.svg delete mode 100644 src/vs/editor/browser/widget/media/close-dark.svg delete mode 100644 src/vs/editor/browser/widget/media/close-light.svg delete mode 100644 src/vs/editor/browser/widget/media/deletion-dark.svg delete mode 100644 src/vs/editor/browser/widget/media/deletion-light.svg delete mode 100644 src/vs/editor/browser/widget/media/lightbulb-dark.svg delete mode 100644 src/vs/editor/browser/widget/media/lightbulb-light.svg diff --git a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts index fc6a255eb83..fb2970e763a 100644 --- a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts +++ b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts @@ -97,7 +97,7 @@ export class LinesDecorationsOverlay extends DedupOverlay { const classNames = toRender[lineIndex]; let lineOutput = ''; for (let i = 0, len = classNames.length; i < len; i++) { - lineOutput += '
` + `
` ]); } } diff --git a/src/vs/editor/browser/widget/inlineDiffMargin.ts b/src/vs/editor/browser/widget/inlineDiffMargin.ts index 90bb8d9165a..6ad8f6c007d 100644 --- a/src/vs/editor/browser/widget/inlineDiffMargin.ts +++ b/src/vs/editor/browser/widget/inlineDiffMargin.ts @@ -57,7 +57,7 @@ export class InlineDiffMargin extends Disposable { this._marginDomNode.style.zIndex = '10'; this._diffActions = document.createElement('div'); - this._diffActions.className = 'lightbulb-glyph'; + this._diffActions.className = 'codicon codicon-lightbulb lightbulb-glyph'; this._diffActions.style.position = 'absolute'; const lineHeight = editor.getOption(EditorOption.lineHeight); const lineFeed = editor.getModel()!.getEOL(); diff --git a/src/vs/editor/browser/widget/media/addition-dark.svg b/src/vs/editor/browser/widget/media/addition-dark.svg deleted file mode 100644 index 4d9389336b9..00000000000 --- a/src/vs/editor/browser/widget/media/addition-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/addition-light.svg b/src/vs/editor/browser/widget/media/addition-light.svg deleted file mode 100644 index 01a9de7d5ab..00000000000 --- a/src/vs/editor/browser/widget/media/addition-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/close-dark.svg b/src/vs/editor/browser/widget/media/close-dark.svg deleted file mode 100644 index 6d16d212ae5..00000000000 --- a/src/vs/editor/browser/widget/media/close-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/close-light.svg b/src/vs/editor/browser/widget/media/close-light.svg deleted file mode 100644 index 742fcae4ae7..00000000000 --- a/src/vs/editor/browser/widget/media/close-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/deletion-dark.svg b/src/vs/editor/browser/widget/media/deletion-dark.svg deleted file mode 100644 index 4c5a9c1e3a5..00000000000 --- a/src/vs/editor/browser/widget/media/deletion-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/deletion-light.svg b/src/vs/editor/browser/widget/media/deletion-light.svg deleted file mode 100644 index d12a8ee3135..00000000000 --- a/src/vs/editor/browser/widget/media/deletion-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/diffEditor.css b/src/vs/editor/browser/widget/media/diffEditor.css index d1d0e48f366..317916a1078 100644 --- a/src/vs/editor/browser/widget/media/diffEditor.css +++ b/src/vs/editor/browser/widget/media/diffEditor.css @@ -37,11 +37,10 @@ .monaco-diff-editor .insert-sign, .monaco-editor .delete-sign, .monaco-diff-editor .delete-sign { - background-size: 60%; - opacity: 0.7; - background-repeat: no-repeat; - background-position: 75% center; - background-size: 11px 11px; + font-size: 11px !important; + opacity: 0.7 !important; + display: flex !important; + align-items: center; } .monaco-editor.hc-black .insert-sign, .monaco-diff-editor.hc-black .insert-sign, @@ -49,27 +48,6 @@ .monaco-diff-editor.hc-black .delete-sign { opacity: 1; } -.monaco-editor .insert-sign, -.monaco-diff-editor .insert-sign { - background-image: url('addition-light.svg'); -} -.monaco-editor .delete-sign, -.monaco-diff-editor .delete-sign { - background-image: url('deletion-light.svg'); -} - -.monaco-editor.vs-dark .insert-sign, -.monaco-diff-editor.vs-dark .insert-sign, -.monaco-editor.hc-black .insert-sign, -.monaco-diff-editor.hc-black .insert-sign { - background-image: url('addition-dark.svg'); -} -.monaco-editor.vs-dark .delete-sign, -.monaco-diff-editor.vs-dark .delete-sign, -.monaco-editor.hc-black .delete-sign, -.monaco-diff-editor.hc-black .delete-sign { - background-image: url('deletion-dark.svg'); -} .monaco-editor .inline-deleted-margin-view-zone { text-align: right; @@ -94,15 +72,6 @@ display: inline-block; } -.monaco-editor .margin-view-zones .inline-deleted-margin-view-zone .lightbulb-glyph { - background: url('lightbulb-light.svg') center center no-repeat; -} - -.monaco-editor.vs-dark .margin-view-zones .inline-deleted-margin-view-zone .lightbulb-glyph, -.monaco-editor.hc-dark .margin-view-zones .inline-deleted-margin-view-zone .lightbulb-glyph { - background: url('lightbulb-dark.svg') center center no-repeat; -} - .monaco-editor .margin-view-zones .lightbulb-glyph:hover { cursor: pointer; } diff --git a/src/vs/editor/browser/widget/media/diffReview.css b/src/vs/editor/browser/widget/media/diffReview.css index f8db5bb1c67..b2b17028489 100644 --- a/src/vs/editor/browser/widget/media/diffReview.css +++ b/src/vs/editor/browser/widget/media/diffReview.css @@ -58,10 +58,3 @@ height: 16px; margin: 2px 0; } -.monaco-diff-editor .action-label.icon.close-diff-review { - background: url('close-light.svg') center center no-repeat; -} -.monaco-diff-editor.hc-black .action-label.icon.close-diff-review, -.monaco-diff-editor.vs-dark .action-label.icon.close-diff-review { - background: url('close-dark.svg') center center no-repeat; -} diff --git a/src/vs/editor/browser/widget/media/lightbulb-dark.svg b/src/vs/editor/browser/widget/media/lightbulb-dark.svg deleted file mode 100644 index 5fe8931a815..00000000000 --- a/src/vs/editor/browser/widget/media/lightbulb-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/lightbulb-light.svg b/src/vs/editor/browser/widget/media/lightbulb-light.svg deleted file mode 100644 index 191c566fd2c..00000000000 --- a/src/vs/editor/browser/widget/media/lightbulb-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - From fc824b9da66d291fa18399f853062a72e0cdf6c5 Mon Sep 17 00:00:00 2001 From: sharkykh Date: Fri, 13 Dec 2019 15:31:48 +0200 Subject: [PATCH 033/843] Restore "current git branch name when renaming" --- extensions/git/src/commands.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index a28161a823c..bbc4c555b19 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1580,7 +1580,7 @@ export class CommandCenter { await this._branch(repository, undefined, true); } - private async promptForBranchName(defaultName?: string): Promise { + private async promptForBranchName(defaultName?: string, initialValue?: string): Promise { const config = workspace.getConfiguration('git'); const branchWhitespaceChar = config.get('branchWhitespaceChar')!; const branchValidationRegex = config.get('branchValidationRegex')!; @@ -1591,6 +1591,7 @@ export class CommandCenter { const rawBranchName = defaultName || await window.showInputBox({ placeHolder: localize('branch name', "Branch name"), prompt: localize('provide branch name', "Please provide a branch name"), + value: initialValue, ignoreFocusOut: true, validateInput: (name: string) => { const validateName = new RegExp(branchValidationRegex); @@ -1668,7 +1669,8 @@ export class CommandCenter { @command('git.renameBranch', { repository: true }) async renameBranch(repository: Repository): Promise { - const branchName = await this.promptForBranchName(); + const currentBranchName = repository.HEAD && repository.HEAD.name; + const branchName = await this.promptForBranchName(undefined, currentBranchName); if (!branchName) { return; From 4553268ad94da3e854cd00dada7987206ad908e2 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 6 Jan 2020 17:50:43 +0100 Subject: [PATCH 034/843] Pass in the line mapper factory to the ViewModel --- .../editor/browser/widget/codeEditorWidget.ts | 3 ++- .../characterHardWrappingLineMapper.ts | 10 ++++++++- .../editor/common/viewModel/viewModelImpl.ts | 22 ++++++++----------- .../test/browser/commands/sideEditing.test.ts | 3 ++- .../test/browser/controller/cursor.test.ts | 15 +++++++------ .../controller/cursorMoveCommand.test.ts | 3 ++- .../test/common/viewModel/testViewModel.ts | 3 ++- 7 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 9c84e1a8062..00d0d4ad3d5 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -50,6 +50,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; let EDITOR_ID = 0; @@ -1329,7 +1330,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE model.onBeforeAttached(); - const viewModel = new ViewModel(this._id, this._configuration, model, (callback) => dom.scheduleAtNextAnimationFrame(callback)); + const viewModel = new ViewModel(this._id, this._configuration, model, CharacterHardWrappingLineMapperFactory.create(this._configuration.options), (callback) => dom.scheduleAtNextAnimationFrame(callback)); listenersToRemove.push(model.onDidChangeDecorations((e) => this._onDidChangeModelDecorations.fire(e))); listenersToRemove.push(model.onDidChangeLanguage((e) => { diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index bbf71e817b9..4b47666617c 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -5,7 +5,7 @@ import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; -import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; +import { WrappingIndent, IComputedEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; import { toUint32Array } from 'vs/base/common/uint'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; @@ -56,6 +56,14 @@ class WrappingCharacterClassifier extends CharacterClassifier { export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactory { + public static create(options: IComputedEditorOptions): CharacterHardWrappingLineMapperFactory { + return new CharacterHardWrappingLineMapperFactory( + options.get(EditorOption.wordWrapBreakBeforeCharacters), + options.get(EditorOption.wordWrapBreakAfterCharacters), + options.get(EditorOption.wordWrapBreakObtrusiveCharacters) + ); + } + private readonly classifier: WrappingCharacterClassifier; constructor(breakBeforeChars: string, breakAfterChars: string, breakObtrusiveChars: string) { diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index e08af64ebf9..d98148d5fab 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -18,8 +18,7 @@ import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTokensColorTracker'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout'; -import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; -import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection, ILineMapperFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; import { ITheme } from 'vs/platform/theme/common/themeService'; @@ -43,7 +42,13 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel public readonly viewLayout: ViewLayout; private readonly decorations: ViewModelDecorations; - constructor(editorId: number, configuration: editorCommon.IConfiguration, model: ITextModel, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { + constructor( + editorId: number, + configuration: editorCommon.IConfiguration, + model: ITextModel, + lineMapperFactory: ILineMapperFactory, + scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable + ) { super(); this.editorId = editorId; @@ -63,20 +68,11 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const options = this.configuration.options; const wrappingInfo = options.get(EditorOption.wrappingInfo); const fontInfo = options.get(EditorOption.fontInfo); - const wordWrapBreakAfterCharacters = options.get(EditorOption.wordWrapBreakAfterCharacters); - const wordWrapBreakBeforeCharacters = options.get(EditorOption.wordWrapBreakBeforeCharacters); - const wordWrapBreakObtrusiveCharacters = options.get(EditorOption.wordWrapBreakObtrusiveCharacters); const wrappingIndent = options.get(EditorOption.wrappingIndent); - let hardWrappingLineMapperFactory = new CharacterHardWrappingLineMapperFactory( - wordWrapBreakBeforeCharacters, - wordWrapBreakAfterCharacters, - wordWrapBreakObtrusiveCharacters - ); - this.lines = new SplitLinesCollection( this.model, - hardWrappingLineMapperFactory, + lineMapperFactory, this.model.getOptions().tabSize, wrappingInfo.wrappingColumn, fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth, diff --git a/src/vs/editor/test/browser/commands/sideEditing.test.ts b/src/vs/editor/test/browser/commands/sideEditing.test.ts index 250f2ff7960..205a169c495 100644 --- a/src/vs/editor/test/browser/commands/sideEditing.test.ts +++ b/src/vs/editor/test/browser/commands/sideEditing.test.ts @@ -14,6 +14,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; +import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; function testCommand(lines: string[], selections: Selection[], edits: IIdentifiedSingleEditOperation[], expectedLines: string[], expectedSelections: Selection[]): void { withTestCodeEditor(lines, {}, (editor, cursor) => { @@ -200,7 +201,7 @@ suite('SideEditing', () => { function _runTest(selection: Selection, editRange: Range, editText: string, editForceMoveMarkers: boolean, expected: Selection, msg: string): void { const model = TextModel.createFromString(LINES.join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, null!); + const viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); const cursor = new Cursor(config, model, viewModel); cursor.setSelections('tests', [selection]); diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index d305b2e5ed8..9031f4056de 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -25,6 +25,7 @@ import { IRelaxedTextModelCreationOptions, createTextModel } from 'vs/editor/tes import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { javascriptOnEnterRules } from 'vs/editor/test/common/modes/supports/javascriptOnEnterRules'; +import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; const H = Handler; @@ -152,7 +153,7 @@ suite('Editor Controller - Cursor', () => { thisModel = createTextModel(text); thisConfiguration = new TestConfiguration({}); - thisViewModel = new ViewModel(0, thisConfiguration, thisModel, null!); + thisViewModel = new ViewModel(0, thisConfiguration, thisModel, CharacterHardWrappingLineMapperFactory.create(thisConfiguration.options), null!); thisCursor = new Cursor(thisConfiguration, thisModel, thisViewModel); }); @@ -776,7 +777,7 @@ suite('Editor Controller - Cursor', () => { 'var newer = require("gulp-newer");', ].join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, null!); + const viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 1, 4, false); @@ -816,7 +817,7 @@ suite('Editor Controller - Cursor', () => { '', ].join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, null!); + const viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 10, 10, false); @@ -880,7 +881,7 @@ suite('Editor Controller - Cursor', () => { '', ].join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, null!); + const viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 10, 10, false); @@ -929,7 +930,7 @@ suite('Editor Controller - Cursor', () => { 'var newer = require("gulp-newer");', ].join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, null!); + const viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 1, 4, false); @@ -2074,7 +2075,7 @@ suite('Editor Controller - Regression tests', () => { wordWrap: 'wordWrapColumn', wordWrapColumn: 100 }); - const viewModel = new ViewModel(0, config, model, null!); + const viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 1, 43, false); @@ -3834,7 +3835,7 @@ function usingCursor(opts: ICursorOpts, callback: (model: TextModel, cursor: Cur let model = createTextModel(opts.text.join('\n'), opts.modelOpts, opts.languageIdentifier); model.forceTokenization(model.getLineCount()); let config = new TestConfiguration(opts.editorOpts || {}); - let viewModel = new ViewModel(0, config, model, null!); + let viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); let cursor = new Cursor(config, model, viewModel); callback(model, cursor); diff --git a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts index a58db2d6c3f..dad6b955cda 100644 --- a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts +++ b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts @@ -13,6 +13,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { TextModel } from 'vs/editor/common/model/textModel'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; +import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; suite('Cursor move command test', () => { @@ -32,7 +33,7 @@ suite('Cursor move command test', () => { thisModel = TextModel.createFromString(text); thisConfiguration = new TestConfiguration({}); - thisViewModel = new ViewModel(0, thisConfiguration, thisModel, null!); + thisViewModel = new ViewModel(0, thisConfiguration, thisModel, CharacterHardWrappingLineMapperFactory.create(thisConfiguration.options), null!); thisCursor = new Cursor(thisConfiguration, thisModel, thisViewModel); }); diff --git a/src/vs/editor/test/common/viewModel/testViewModel.ts b/src/vs/editor/test/common/viewModel/testViewModel.ts index 75b429dd095..6513d075b5e 100644 --- a/src/vs/editor/test/common/viewModel/testViewModel.ts +++ b/src/vs/editor/test/common/viewModel/testViewModel.ts @@ -7,6 +7,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { TextModel } from 'vs/editor/common/model/textModel'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; +import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; export function testViewModel(text: string[], options: IEditorOptions, callback: (viewModel: ViewModel, model: TextModel) => void): void { const EDITOR_ID = 1; @@ -15,7 +16,7 @@ export function testViewModel(text: string[], options: IEditorOptions, callback: let model = TextModel.createFromString(text.join('\n')); - let viewModel = new ViewModel(EDITOR_ID, configuration, model, null!); + let viewModel = new ViewModel(EDITOR_ID, configuration, model, CharacterHardWrappingLineMapperFactory.create(configuration.options), null!); callback(viewModel, model); From adecd91a680f959ac6d8642ad75c80cdf0dca312 Mon Sep 17 00:00:00 2001 From: RealZogger <49367953+RealZogger@users.noreply.github.com> Date: Mon, 6 Jan 2020 18:34:35 +0000 Subject: [PATCH 035/843] fix missing strikethrough styling for scm resource states. fixes #87668 --- src/vs/workbench/contrib/scm/browser/media/scmViewlet.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css index 5c4f41d1fdf..5b446fb53c3 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css +++ b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css @@ -114,7 +114,7 @@ overflow: hidden; } -.scm-viewlet .monaco-list-row .resource > .name.strike-through > .monaco-icon-label > .monaco-icon-label-description-container > .label-name { +.scm-viewlet .monaco-list-row .resource > .name.strike-through > .monaco-icon-label > .monaco-icon-label-container > .monaco-icon-name-container > .label-name { text-decoration: line-through; } From 2c15f88cd55dffc7372d0b8ab5c9e910e78daa2d Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 10:22:40 +0100 Subject: [PATCH 036/843] Have line mappings be computed in batches --- .../characterHardWrappingLineMapper.ts | 20 +++++- .../common/viewModel/splitLinesCollection.ts | 61 +++++++++++++------ .../editor/common/viewModel/viewModelImpl.ts | 32 ++++++++-- .../characterHardWrappingLineMapper.test.ts | 5 +- 4 files changed, 94 insertions(+), 24 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 4b47666617c..beea12ee74f 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -9,7 +9,7 @@ import { WrappingIndent, IComputedEditorOptions, EditorOption } from 'vs/editor/ import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; import { toUint32Array } from 'vs/base/common/uint'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { ILineMapperFactory, ILineMapping, OutputPosition } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { ILineMapperFactory, ILineMapping, OutputPosition, ILineMappingComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; const enum CharacterClass { NONE = 0, @@ -82,7 +82,23 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor return currentVisibleColumn + columnSize; } - public createLineMapping(lineText: string, tabSize: number, breakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): ILineMapping | null { + public createLineMappingComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineMappingComputer { + let requests: string[] = []; + return { + addRequest: (lineText: string) => { + requests.push(lineText); + }, + finalize: () => { + let result: (ILineMapping | null)[] = []; + for (let i = 0, len = requests.length; i < len; i++) { + result[i] = this._createLineMapping(requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + } + return result; + } + }; + } + + private _createLineMapping(lineText: string, tabSize: number, breakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): ILineMapping | null { if (breakingColumn === -1) { return null; } diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 3cd2afbafaf..833aabad313 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -33,8 +33,13 @@ export interface ILineMapping { getOutputPositionOfInputOffset(inputOffset: number): OutputPosition; } +export interface ILineMappingComputer { + addRequest(lineText: string): void; + finalize(): (ILineMapping | null)[]; +} + export interface ILineMapperFactory { - createLineMapping(lineText: string, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineMapping | null; + createLineMappingComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineMappingComputer; } export interface ISimpleModel { @@ -71,10 +76,11 @@ export interface IViewModelLinesCollection extends IDisposable { getHiddenAreas(): Range[]; setHiddenAreas(_ranges: Range[]): boolean; + createLineMappingComputer(): ILineMappingComputer; onModelFlushed(): void; onModelLinesDeleted(versionId: number, fromLineNumber: number, toLineNumber: number): viewEvents.ViewLinesDeletedEvent | null; - onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, text: string[]): viewEvents.ViewLinesInsertedEvent | null; - onModelLineChanged(versionId: number, lineNumber: number, newText: string): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null]; + onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, linesMappings: (ILineMapping | null)[]): viewEvents.ViewLinesInsertedEvent | null; + onModelLineChanged(versionId: number, lineNumber: number, lineMapping: ILineMapping | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null]; acceptVersionId(versionId: number): void; getViewLineCount(): number; @@ -201,7 +207,13 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } let linesContent = this.model.getLinesContent(); - let lineCount = linesContent.length; + const lineCount = linesContent.length; + const lineMappingComputer = this.linePositionMapperFactory.createLineMappingComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent); + for (let i = 0; i < lineCount; i++) { + lineMappingComputer.addRequest(linesContent[i]); + } + const lineMappings = lineMappingComputer.finalize(); + let values = new Uint32Array(lineCount); let hiddenAreas = this.hiddenAreasIds.map((areaId) => this.model.getDecorationRange(areaId)!).sort(Range.compareRangesUsingStarts); @@ -220,7 +232,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } let isInHiddenArea = (lineNumber >= hiddenAreaStart && lineNumber <= hiddenAreaEnd); - let line = createSplitLine(this.linePositionMapperFactory, linesContent[i], this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent, !isInHiddenArea); + let line = createSplitLine(lineMappings[i], !isInHiddenArea); values[i] = line.getViewLineCount(); this.lines[i] = line; } @@ -370,6 +382,10 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return true; } + public createLineMappingComputer(): ILineMappingComputer { + return this.linePositionMapperFactory.createLineMappingComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent); + } + public onModelFlushed(): void { this._constructLines(true); } @@ -390,7 +406,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return new viewEvents.ViewLinesDeletedEvent(outputFromLineNumber, outputToLineNumber); } - public onModelLinesInserted(versionId: number, fromLineNumber: number, _toLineNumber: number, text: string[]): viewEvents.ViewLinesInsertedEvent | null { + public onModelLinesInserted(versionId: number, fromLineNumber: number, _toLineNumber: number, linesMappings: (ILineMapping | null)[]): viewEvents.ViewLinesInsertedEvent | null { if (versionId <= this._validModelVersionId) { // Here we check for versionId in case the lines were reconstructed in the meantime. // We don't want to apply stale change events on top of a newer read model state. @@ -411,10 +427,10 @@ export class SplitLinesCollection implements IViewModelLinesCollection { let totalOutputLineCount = 0; let insertLines: ISplitLine[] = []; - let insertPrefixSumValues = new Uint32Array(text.length); + let insertPrefixSumValues = new Uint32Array(linesMappings.length); - for (let i = 0, len = text.length; i < len; i++) { - let line = createSplitLine(this.linePositionMapperFactory, text[i], this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent, !isInHiddenArea); + for (let i = 0, len = linesMappings.length; i < len; i++) { + let line = createSplitLine(linesMappings[i], !isInHiddenArea); insertLines.push(line); let outputLineCount = line.getViewLineCount(); @@ -430,7 +446,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return new viewEvents.ViewLinesInsertedEvent(outputFromLineNumber, outputFromLineNumber + totalOutputLineCount - 1); } - public onModelLineChanged(versionId: number, lineNumber: number, newText: string): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { + public onModelLineChanged(versionId: number, lineNumber: number, lineMapping: ILineMapping | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { if (versionId <= this._validModelVersionId) { // Here we check for versionId in case the lines were reconstructed in the meantime. // We don't want to apply stale change events on top of a newer read model state. @@ -441,7 +457,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { let oldOutputLineCount = this.lines[lineIndex].getViewLineCount(); let isVisible = this.lines[lineIndex].isVisible(); - let line = createSplitLine(this.linePositionMapperFactory, newText, this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent, isVisible); + let line = createSplitLine(lineMapping, isVisible); this.lines[lineIndex] = line; let newOutputLineCount = this.lines[lineIndex].getViewLineCount(); @@ -1199,16 +1215,15 @@ export class SplitLine implements ISplitLine { } } -function createSplitLine(linePositionMapperFactory: ILineMapperFactory, text: string, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, isVisible: boolean): ISplitLine { - let positionMapper = linePositionMapperFactory.createLineMapping(text, tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); - if (positionMapper === null) { +function createSplitLine(lineMapping: ILineMapping | null, isVisible: boolean): ISplitLine { + if (lineMapping === null) { // No mapping needed if (isVisible) { return VisibleIdentitySplitLine.INSTANCE; } return InvisibleIdentitySplitLine.INSTANCE; } else { - return new SplitLine(positionMapper, isVisible); + return new SplitLine(lineMapping, isVisible); } } @@ -1298,6 +1313,18 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return false; } + public createLineMappingComputer(): ILineMappingComputer { + let result: null[] = []; + return { + addRequest: (lineText: string) => { + result.push(null); + }, + finalize: () => { + return result; + } + }; + } + public onModelFlushed(): void { } @@ -1305,11 +1332,11 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return new viewEvents.ViewLinesDeletedEvent(fromLineNumber, toLineNumber); } - public onModelLinesInserted(_versionId: number, fromLineNumber: number, toLineNumber: number, _text: string[]): viewEvents.ViewLinesInsertedEvent | null { + public onModelLinesInserted(_versionId: number, fromLineNumber: number, toLineNumber: number, linesMappings: (ILineMapping | null)[]): viewEvents.ViewLinesInsertedEvent | null { return new viewEvents.ViewLinesInsertedEvent(fromLineNumber, toLineNumber); } - public onModelLineChanged(_versionId: number, lineNumber: number, _newText: string): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { + public onModelLineChanged(_versionId: number, lineNumber: number, lineMapping: ILineMapping | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { return [false, new viewEvents.ViewLinesChangedEvent(lineNumber, lineNumber), null, null]; } diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index d98148d5fab..3bb303641c7 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -196,8 +196,26 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const changes = e.changes; const versionId = e.versionId; - for (let j = 0, lenJ = changes.length; j < lenJ; j++) { - const change = changes[j]; + // Do a first pass to compute line mappings, and a second pass to actually interpret them + const lineMappingComputer = this.lines.createLineMappingComputer(); + for (const change of changes) { + switch (change.changeType) { + case textModelEvents.RawContentChangedType.LinesInserted: { + for (const line of change.detail) { + lineMappingComputer.addRequest(line); + } + break; + } + case textModelEvents.RawContentChangedType.LineChanged: { + lineMappingComputer.addRequest(change.detail); + break; + } + } + } + const lineMappings = lineMappingComputer.finalize(); + let lineMappingsOffset = 0; + + for (const change of changes) { switch (change.changeType) { case textModelEvents.RawContentChangedType.Flush: { @@ -218,7 +236,10 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel break; } case textModelEvents.RawContentChangedType.LinesInserted: { - const linesInsertedEvent = this.lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, change.detail); + const insertedLinesMappings = lineMappings.slice(lineMappingsOffset, lineMappingsOffset + change.detail.length); + lineMappingsOffset += change.detail.length; + + const linesInsertedEvent = this.lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLinesMappings); if (linesInsertedEvent !== null) { eventsCollector.emit(linesInsertedEvent); this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber); @@ -227,7 +248,10 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel break; } case textModelEvents.RawContentChangedType.LineChanged: { - const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this.lines.onModelLineChanged(versionId, change.lineNumber, change.detail); + const changedLineMapping = lineMappings[lineMappingsOffset]; + lineMappingsOffset++; + + const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this.lines.onModelLineChanged(versionId, change.lineNumber, changedLineMapping); hadModelLineChangeThatChangedLineMapping = lineMappingChanged; if (linesChangedEvent) { eventsCollector.emit(linesChangedEvent); diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts index aec12368fa1..2c498980c86 100644 --- a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts +++ b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts @@ -21,7 +21,10 @@ function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAf } } - const mapper = factory.createLineMapping(rawText, tabSize, breakAfter, 2, wrappingIndent); + const lineMappingComputer = factory.createLineMappingComputer(tabSize, breakAfter, 2, wrappingIndent); + lineMappingComputer.addRequest(rawText); + const lineMappings = lineMappingComputer.finalize(); + const mapper = lineMappings[0]; // Insert line break markers again, according to algorithm let actualAnnotatedText = ''; From 47d359935ffc07779b4ebd1f5f24b10885bbae32 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 10:28:45 +0100 Subject: [PATCH 037/843] Save a visibleRangeForPosition call if possible --- .../viewParts/indentGuides/indentGuides.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts index 6af61888a62..33979994f9d 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts @@ -136,14 +136,16 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { const indent = indents[lineIndex]; let result = ''; - const leftMostVisiblePosition = ctx.visibleRangeForPosition(new Position(lineNumber, 1)); - let left = leftMostVisiblePosition ? leftMostVisiblePosition.left : 0; - for (let i = 1; i <= indent; i++) { - const className = (containsActiveIndentGuide && i === activeIndentLevel ? 'cigra' : 'cigr'); - result += `
`; - left += indentWidth; - if (left > scrollWidth || (this._maxIndentLeft > 0 && left > this._maxIndentLeft)) { - break; + if (indent >= 1) { + const leftMostVisiblePosition = ctx.visibleRangeForPosition(new Position(lineNumber, 1)); + let left = leftMostVisiblePosition ? leftMostVisiblePosition.left : 0; + for (let i = 1; i <= indent; i++) { + const className = (containsActiveIndentGuide && i === activeIndentLevel ? 'cigra' : 'cigr'); + result += `
`; + left += indentWidth; + if (left > scrollWidth || (this._maxIndentLeft > 0 && left > this._maxIndentLeft)) { + break; + } } } From 09d3bbf788cb2c09509dcf7152657e23a3a3307a Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 10:59:21 +0100 Subject: [PATCH 038/843] Speed up the common case in WrappingCharacterClassifier (#21196) --- .../editor/common/core/characterClassifier.ts | 6 ++-- .../characterHardWrappingLineMapper.ts | 28 +++++++++++-------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/common/core/characterClassifier.ts b/src/vs/editor/common/core/characterClassifier.ts index 214bb2abf05..e5aac27c816 100644 --- a/src/vs/editor/common/core/characterClassifier.ts +++ b/src/vs/editor/common/core/characterClassifier.ts @@ -12,14 +12,14 @@ export class CharacterClassifier { /** * Maintain a compact (fully initialized ASCII map for quickly classifying ASCII characters - used more often in code). */ - private _asciiMap: Uint8Array; + protected _asciiMap: Uint8Array; /** * The entire map (sparse array). */ - private _map: Map; + protected _map: Map; - private _defaultValue: number; + protected _defaultValue: number; constructor(_defaultValue: T) { let defaultValue = toUint8(_defaultValue); diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index beea12ee74f..a9f91d025dc 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -38,19 +38,23 @@ class WrappingCharacterClassifier extends CharacterClassifier { } public get(charCode: number): CharacterClass { - // Initialize CharacterClass.BREAK_IDEOGRAPHIC for these Unicode ranges: - // 1. CJK Unified Ideographs (0x4E00 -- 0x9FFF) - // 2. CJK Unified Ideographs Extension A (0x3400 -- 0x4DBF) - // 3. Hiragana and Katakana (0x3040 -- 0x30FF) - if ( - (charCode >= 0x3040 && charCode <= 0x30FF) - || (charCode >= 0x3400 && charCode <= 0x4DBF) - || (charCode >= 0x4E00 && charCode <= 0x9FFF) - ) { - return CharacterClass.BREAK_IDEOGRAPHIC; - } + if (charCode >= 0 && charCode < 256) { + return this._asciiMap[charCode]; + } else { + // Initialize CharacterClass.BREAK_IDEOGRAPHIC for these Unicode ranges: + // 1. CJK Unified Ideographs (0x4E00 -- 0x9FFF) + // 2. CJK Unified Ideographs Extension A (0x3400 -- 0x4DBF) + // 3. Hiragana and Katakana (0x3040 -- 0x30FF) + if ( + (charCode >= 0x3040 && charCode <= 0x30FF) + || (charCode >= 0x3400 && charCode <= 0x4DBF) + || (charCode >= 0x4E00 && charCode <= 0x9FFF) + ) { + return CharacterClass.BREAK_IDEOGRAPHIC; + } - return super.get(charCode); + return (this._map.get(charCode) || this._defaultValue); + } } } From 57758d6750390feef59d8a863d930070813eb2bc Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 11:15:40 +0100 Subject: [PATCH 039/843] Remove obtrusive wrapping characters since it was used only for dot (#21196) --- src/vs/editor/common/config/editorOptions.ts | 16 ++------- .../characterHardWrappingLineMapper.ts | 36 +++---------------- .../characterHardWrappingLineMapper.test.ts | 28 +++++++-------- .../viewModel/splitLinesCollection.test.ts | 8 ++--- src/vs/monaco.d.ts | 9 ++--- 5 files changed, 26 insertions(+), 71 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 3adac8cf46b..94af6b10d1b 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -264,19 +264,14 @@ export interface IEditorOptions { wrappingIndent?: 'none' | 'same' | 'indent' | 'deepIndent'; /** * Configure word wrapping characters. A break will be introduced before these characters. - * Defaults to '{([+'. + * Defaults to '([{‘“〈《「『【〔([{「£¥$£¥++'. */ wordWrapBreakBeforeCharacters?: string; /** * Configure word wrapping characters. A break will be introduced after these characters. - * Defaults to ' \t})]?|&,;'. + * Defaults to ' \t})]?|/&.,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」'. */ wordWrapBreakAfterCharacters?: string; - /** - * Configure word wrapping characters. A break will be introduced after these characters only if no `wordWrapBreakBeforeCharacters` or `wordWrapBreakAfterCharacters` were found. - * Defaults to '.'. - */ - wordWrapBreakObtrusiveCharacters?: string; /** * Performance guard: Stop rendering a line after x characters. * Defaults to 10000. @@ -3154,7 +3149,6 @@ export const enum EditorOption { wordWrap, wordWrapBreakAfterCharacters, wordWrapBreakBeforeCharacters, - wordWrapBreakObtrusiveCharacters, wordWrapColumn, wordWrapMinified, wrappingIndent, @@ -3681,16 +3675,12 @@ export const EditorOptions = { )), wordWrapBreakAfterCharacters: register(new EditorStringOption( EditorOption.wordWrapBreakAfterCharacters, 'wordWrapBreakAfterCharacters', - ' \t})]?|/&,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」', + ' \t})]?|/&.,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」', )), wordWrapBreakBeforeCharacters: register(new EditorStringOption( EditorOption.wordWrapBreakBeforeCharacters, 'wordWrapBreakBeforeCharacters', '([{‘“〈《「『【〔([{「£¥$£¥++' )), - wordWrapBreakObtrusiveCharacters: register(new EditorStringOption( - EditorOption.wordWrapBreakObtrusiveCharacters, 'wordWrapBreakObtrusiveCharacters', - '.' - )), wordWrapColumn: register(new EditorIntOption( EditorOption.wordWrapColumn, 'wordWrapColumn', 80, 1, Constants.MAX_SAFE_SMALL_INTEGER, diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index a9f91d025dc..6053822efa5 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -15,13 +15,12 @@ const enum CharacterClass { NONE = 0, BREAK_BEFORE = 1, BREAK_AFTER = 2, - BREAK_OBTRUSIVE = 3, - BREAK_IDEOGRAPHIC = 4 // for Han and Kana. + BREAK_IDEOGRAPHIC = 3 // for Han and Kana. } class WrappingCharacterClassifier extends CharacterClassifier { - constructor(BREAK_BEFORE: string, BREAK_AFTER: string, BREAK_OBTRUSIVE: string) { + constructor(BREAK_BEFORE: string, BREAK_AFTER: string) { super(CharacterClass.NONE); for (let i = 0; i < BREAK_BEFORE.length; i++) { @@ -31,10 +30,6 @@ class WrappingCharacterClassifier extends CharacterClassifier { for (let i = 0; i < BREAK_AFTER.length; i++) { this.set(BREAK_AFTER.charCodeAt(i), CharacterClass.BREAK_AFTER); } - - for (let i = 0; i < BREAK_OBTRUSIVE.length; i++) { - this.set(BREAK_OBTRUSIVE.charCodeAt(i), CharacterClass.BREAK_OBTRUSIVE); - } } public get(charCode: number): CharacterClass { @@ -63,15 +58,14 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor public static create(options: IComputedEditorOptions): CharacterHardWrappingLineMapperFactory { return new CharacterHardWrappingLineMapperFactory( options.get(EditorOption.wordWrapBreakBeforeCharacters), - options.get(EditorOption.wordWrapBreakAfterCharacters), - options.get(EditorOption.wordWrapBreakObtrusiveCharacters) + options.get(EditorOption.wordWrapBreakAfterCharacters) ); } private readonly classifier: WrappingCharacterClassifier; - constructor(breakBeforeChars: string, breakAfterChars: string, breakObtrusiveChars: string) { - this.classifier = new WrappingCharacterClassifier(breakBeforeChars, breakAfterChars, breakObtrusiveChars); + constructor(breakBeforeChars: string, breakAfterChars: string) { + this.classifier = new WrappingCharacterClassifier(breakBeforeChars, breakAfterChars); } // TODO@Alex -> duplicated in lineCommentCommand @@ -152,8 +146,6 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor let visibleColumn = 0; // Visible column since the beginning of the current line let niceBreakOffset = -1; // Last index of a character that indicates a break should happen before it (more desirable) let niceBreakVisibleColumn = 0; // visible column if a break were to be later introduced before `niceBreakOffset` - let obtrusiveBreakOffset = -1; // Last index of a character that indicates a break should happen before it (less desirable) - let obtrusiveBreakVisibleColumn = 0; // visible column if a break were to be later introduced before `obtrusiveBreakOffset` let len = lineText.length; for (let i = 0; i < len; i++) { @@ -212,12 +204,6 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor breakBeforeOffset = niceBreakOffset; restoreVisibleColumnFrom = niceBreakVisibleColumn; - } else if (obtrusiveBreakOffset !== -1 && obtrusiveBreakVisibleColumn <= breakingColumn) { - - // We will break before `obtrusiveBreakLastOffset` - breakBeforeOffset = obtrusiveBreakOffset; - restoreVisibleColumnFrom = obtrusiveBreakVisibleColumn; - } else { // We will break before `i` @@ -236,8 +222,6 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor // Reset markers niceBreakOffset = -1; niceBreakVisibleColumn = 0; - obtrusiveBreakOffset = -1; - obtrusiveBreakVisibleColumn = 0; } // At this point, there is a certainty that the character at `i` fits on the current line @@ -246,10 +230,6 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor // Advance niceBreakVisibleColumn niceBreakVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(niceBreakVisibleColumn, tabSize, charCodeIsTab, charColumnSize); } - if (obtrusiveBreakOffset !== -1) { - // Advance obtrusiveBreakVisibleColumn - obtrusiveBreakVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(obtrusiveBreakVisibleColumn, tabSize, charCodeIsTab, charColumnSize); - } if (charCodeClass === CharacterClass.BREAK_AFTER && (hardWrappingIndent === WrappingIndent.None || i >= firstNonWhitespaceIndex)) { // This is a character that indicates that a break should happen after it @@ -266,12 +246,6 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; } } - - if (charCodeClass === CharacterClass.BREAK_OBTRUSIVE) { - // This is an obtrusive character that indicates that a break should happen after it - obtrusiveBreakOffset = i + 1; - obtrusiveBreakVisibleColumn = wrappedTextIndentVisibleColumn; - } } if (breakingLengthsIndex === 0) { diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts index 2c498980c86..4195ba5544e 100644 --- a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts +++ b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts @@ -51,7 +51,7 @@ function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAf suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { test('CharacterHardWrappingLineMapper', () => { - let factory = new CharacterHardWrappingLineMapperFactory('(', ')', '.'); + let factory = new CharacterHardWrappingLineMapperFactory('(', ').'); // Empty string assertLineMapping(factory, 4, 5, ''); @@ -64,7 +64,7 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { // Acts like hard wrapping if no char found assertLineMapping(factory, 4, 5, 'aaaaa|a'); - // Honors obtrusive wrapping character + // Honors wrapping character assertLineMapping(factory, 4, 5, 'aaaaa|.'); assertLineMapping(factory, 4, 5, 'aaaaa|a.|aaa.|aa'); assertLineMapping(factory, 4, 5, 'aaaaa|a..|aaa.|aa'); @@ -80,19 +80,19 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { // Honors wrapping before characters (& gives it priority) assertLineMapping(factory, 4, 5, 'aaa.|aa'); - assertLineMapping(factory, 4, 5, 'aaa|(.aa'); + assertLineMapping(factory, 4, 5, 'aaa(.|aa'); // Honors wrapping after characters (& gives it priority) assertLineMapping(factory, 4, 5, 'aaa))|).aaa'); - assertLineMapping(factory, 4, 5, 'aaa))|)|.aaaa'); - assertLineMapping(factory, 4, 5, 'aaa)|()|.aaa'); - assertLineMapping(factory, 4, 5, 'aaa(|()|.aaa'); - assertLineMapping(factory, 4, 5, 'aa.(|()|.aaa'); - assertLineMapping(factory, 4, 5, 'aa.|(.)|.aaa'); + assertLineMapping(factory, 4, 5, 'aaa))|).|aaaa'); + assertLineMapping(factory, 4, 5, 'aaa)|().|aaa'); + assertLineMapping(factory, 4, 5, 'aaa(|().|aaa'); + assertLineMapping(factory, 4, 5, 'aa.(|().|aaa'); + assertLineMapping(factory, 4, 5, 'aa.(.|).aaa'); }); test('CharacterHardWrappingLineMapper - CJK and Kinsoku Shori', () => { - let factory = new CharacterHardWrappingLineMapperFactory('(', ')', '.'); + let factory = new CharacterHardWrappingLineMapperFactory('(', ')'); assertLineMapping(factory, 4, 5, 'aa \u5b89|\u5b89'); assertLineMapping(factory, 4, 5, '\u3042 \u5b89|\u5b89'); assertLineMapping(factory, 4, 5, '\u3042\u3042|\u5b89\u5b89'); @@ -102,28 +102,28 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { }); test('CharacterHardWrappingLineMapper - WrappingIndent.Same', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', ' ', ''); + let factory = new CharacterHardWrappingLineMapperFactory('', ' '); assertLineMapping(factory, 4, 38, ' *123456789012345678901234567890123456|7890', WrappingIndent.Same); }); test('issue #16332: Scroll bar overlaying on top of text', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', ' ', ''); + let factory = new CharacterHardWrappingLineMapperFactory('', ' '); assertLineMapping(factory, 4, 24, 'a/ very/long/line/of/tex|t/that/expands/beyon|d/your/typical/line/|of/code/', WrappingIndent.Indent); }); test('issue #35162: wrappingIndent not consistently working', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', ' ', ''); + let factory = new CharacterHardWrappingLineMapperFactory('', ' '); let mapper = assertLineMapping(factory, 4, 24, ' t h i s |i s |a l |o n |g l |i n |e', WrappingIndent.Indent); assert.equal(mapper!.getWrappedLinesIndent(), ' \t'); }); test('issue #75494: surrogate pairs', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', ' ', ''); + let factory = new CharacterHardWrappingLineMapperFactory('', ' '); assertLineMapping(factory, 4, 49, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇|👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬', WrappingIndent.Same); }); test('CharacterHardWrappingLineMapper - WrappingIndent.DeepIndent', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', ' ', ''); + let factory = new CharacterHardWrappingLineMapperFactory('', ' '); let mapper = assertLineMapping(factory, 4, 26, ' W e A r e T e s t |i n g D e |e p I n d |e n t a t |i o n', WrappingIndent.DeepIndent); assert.equal(mapper!.getWrappedLinesIndent(), ' \t\t'); }); diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index a8b64c4da7a..be941b900e1 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -95,13 +95,11 @@ suite('Editor ViewModel - SplitLinesCollection', () => { const fontInfo = config.options.get(EditorOption.fontInfo); const wordWrapBreakAfterCharacters = config.options.get(EditorOption.wordWrapBreakAfterCharacters); const wordWrapBreakBeforeCharacters = config.options.get(EditorOption.wordWrapBreakBeforeCharacters); - const wordWrapBreakObtrusiveCharacters = config.options.get(EditorOption.wordWrapBreakObtrusiveCharacters); const wrappingIndent = config.options.get(EditorOption.wrappingIndent); const hardWrappingLineMapperFactory = new CharacterHardWrappingLineMapperFactory( wordWrapBreakBeforeCharacters, - wordWrapBreakAfterCharacters, - wordWrapBreakObtrusiveCharacters + wordWrapBreakAfterCharacters ); const model = TextModel.createFromString([ @@ -749,13 +747,11 @@ suite('SplitLinesCollection', () => { const fontInfo = configuration.options.get(EditorOption.fontInfo); const wordWrapBreakAfterCharacters = configuration.options.get(EditorOption.wordWrapBreakAfterCharacters); const wordWrapBreakBeforeCharacters = configuration.options.get(EditorOption.wordWrapBreakBeforeCharacters); - const wordWrapBreakObtrusiveCharacters = configuration.options.get(EditorOption.wordWrapBreakObtrusiveCharacters); const wrappingIndent = configuration.options.get(EditorOption.wrappingIndent); const factory = new CharacterHardWrappingLineMapperFactory( wordWrapBreakBeforeCharacters, - wordWrapBreakAfterCharacters, - wordWrapBreakObtrusiveCharacters + wordWrapBreakAfterCharacters ); const linesCollection = new SplitLinesCollection( diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 32a3f131d6b..9c9b413ede1 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2709,19 +2709,14 @@ declare namespace monaco.editor { wrappingIndent?: 'none' | 'same' | 'indent' | 'deepIndent'; /** * Configure word wrapping characters. A break will be introduced before these characters. - * Defaults to '{([+'. + * Defaults to '([{‘“〈《「『【〔([{「£¥$£¥++'. */ wordWrapBreakBeforeCharacters?: string; /** * Configure word wrapping characters. A break will be introduced after these characters. - * Defaults to ' \t})]?|&,;'. + * Defaults to ' \t})]?|/&.,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」'. */ wordWrapBreakAfterCharacters?: string; - /** - * Configure word wrapping characters. A break will be introduced after these characters only if no `wordWrapBreakBeforeCharacters` or `wordWrapBreakAfterCharacters` were found. - * Defaults to '.'. - */ - wordWrapBreakObtrusiveCharacters?: string; /** * Performance guard: Stop rendering a line after x characters. * Defaults to 10000. From ce3a96347a738753ac9be68bcfc17b315e5e9290 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 11:23:17 +0100 Subject: [PATCH 040/843] :lipstick: --- .../characterHardWrappingLineMapper.ts | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 6053822efa5..5e8265cf0b6 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -139,22 +139,22 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor } } - let classifier = this.classifier; + const classifier = this.classifier; let lastBreakingOffset = 0; // Last 0-based offset in the lineText at which a break happened let breakingLengths: number[] = []; // The length of each broken-up line text let breakingLengthsIndex: number = 0; // The count of breaks already done let visibleColumn = 0; // Visible column since the beginning of the current line let niceBreakOffset = -1; // Last index of a character that indicates a break should happen before it (more desirable) let niceBreakVisibleColumn = 0; // visible column if a break were to be later introduced before `niceBreakOffset` - let len = lineText.length; + const len = lineText.length; for (let i = 0; i < len; i++) { // At this point, there is a certainty that the character before `i` fits on the current line, // but the character at `i` might not fit - let charCode = lineText.charCodeAt(i); - let charCodeIsTab = (charCode === CharCode.Tab); - let charCodeClass = classifier.get(charCode); + const charCode = lineText.charCodeAt(i); + const charCodeIsTab = (charCode === CharCode.Tab); + const charCodeClass = classifier.get(charCode); if (strings.isLowSurrogate(charCode)/* && i + 1 < len */) { // A surrogate pair must always be considered as a single unit, so it is never to be broken @@ -173,18 +173,14 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor // CJK breaking : before break if (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i > 0) { - let prevCode = lineText.charCodeAt(i - 1); - let prevClass = classifier.get(prevCode); + const prevClass = classifier.get(lineText.charCodeAt(i - 1)); if (prevClass !== CharacterClass.BREAK_BEFORE) { // Kinsoku Shori: Don't break after a leading character, like an open bracket niceBreakOffset = i; niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; } } - let charColumnSize = 1; - if (strings.isFullWidthCharacter(charCode)) { - charColumnSize = columnsForFullWidthChar; - } + const charColumnSize = strings.isFullWidthCharacter(charCode) ? columnsForFullWidthChar : 1; // Advance visibleColumn with character at `i` visibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(visibleColumn, tabSize, charCodeIsTab, charColumnSize); @@ -239,8 +235,7 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor // CJK breaking : after break if (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i < len - 1) { - let nextCode = lineText.charCodeAt(i + 1); - let nextClass = classifier.get(nextCode); + const nextClass = classifier.get(lineText.charCodeAt(i + 1)); if (nextClass !== CharacterClass.BREAK_AFTER) { // Kinsoku Shori: Don't break before a trailing character, like a period niceBreakOffset = i + 1; niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; From 881c21693d6c783e8cecd41265760d324c1965e3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 7 Jan 2020 11:25:47 +0100 Subject: [PATCH 041/843] render debug labels --- .../contrib/bulkEdit/browser/bulkEditTree.ts | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 7fee573a6dc..321eb64bf45 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -17,18 +17,25 @@ import * as dom from 'vs/base/browser/dom'; import { ITextModel } from 'vs/editor/common/model'; import { IDisposable } from 'vs/base/common/lifecycle'; import { TextModel } from 'vs/editor/common/model/textModel'; +import { ILabelService } from 'vs/platform/label/common/label'; // --- VIEW MODEL export class FileElement { - constructor(readonly edit: modes.ResourceFileEdit | modes.ResourceTextEdit) { } + readonly uri: URI; + readonly _debugName: string; - getUri(): URI { - return modes.isResourceTextEdit(this.edit) + constructor(readonly edit: modes.ResourceFileEdit | modes.ResourceTextEdit) { + this.uri = modes.isResourceTextEdit(this.edit) ? this.edit.resource : this.edit.oldUri || this.edit.newUri!; + + this._debugName = modes.isResourceTextEdit(this.edit) + ? 'text edit' + : this.edit.oldUri && this.edit.newUri ? 'rename' : this.edit.newUri ? 'create' : 'delete'; } + } export class TextEditElement { @@ -135,18 +142,32 @@ export class FileElementRenderer implements ITreeRenderer, _index: number, template: FileElementTemplate): void { - template.label.setFile(node.element.getUri(), { matches: createMatches(node.filterData) }); + + template.label.setResource({ + name: this._labelService.getUriLabel(node.element.uri, { relative: true }), + description: node.element._debugName, + resource: node.element.uri, + }, { + matches: createMatches(node.filterData), + }); } disposeTemplate(template: FileElementTemplate): void { From fd628855e1a1bccf3ee8266c767a6c4279705dce Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 11:57:32 +0100 Subject: [PATCH 042/843] Fix \t as BREAK_AFTER in tests --- .../characterHardWrappingLineMapper.test.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts index 4195ba5544e..ac4c75c2dae 100644 --- a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts +++ b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts @@ -51,7 +51,7 @@ function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAf suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { test('CharacterHardWrappingLineMapper', () => { - let factory = new CharacterHardWrappingLineMapperFactory('(', ').'); + let factory = new CharacterHardWrappingLineMapperFactory('(', '\t).'); // Empty string assertLineMapping(factory, 4, 5, ''); @@ -73,10 +73,10 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { // Honors tabs when computing wrapping position assertLineMapping(factory, 4, 5, '\t'); - assertLineMapping(factory, 4, 5, '\ta|aa'); - assertLineMapping(factory, 4, 5, '\ta|\ta|a'); + assertLineMapping(factory, 4, 5, '\t|aaa'); + assertLineMapping(factory, 4, 5, '\t|a\t|aa'); assertLineMapping(factory, 4, 5, 'aa\ta'); - assertLineMapping(factory, 4, 5, 'aa\ta|a'); + assertLineMapping(factory, 4, 5, 'aa\t|aa'); // Honors wrapping before characters (& gives it priority) assertLineMapping(factory, 4, 5, 'aaa.|aa'); @@ -92,7 +92,7 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { }); test('CharacterHardWrappingLineMapper - CJK and Kinsoku Shori', () => { - let factory = new CharacterHardWrappingLineMapperFactory('(', ')'); + let factory = new CharacterHardWrappingLineMapperFactory('(', '\t)'); assertLineMapping(factory, 4, 5, 'aa \u5b89|\u5b89'); assertLineMapping(factory, 4, 5, '\u3042 \u5b89|\u5b89'); assertLineMapping(factory, 4, 5, '\u3042\u3042|\u5b89\u5b89'); @@ -102,28 +102,28 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { }); test('CharacterHardWrappingLineMapper - WrappingIndent.Same', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', ' '); + let factory = new CharacterHardWrappingLineMapperFactory('', '\t '); assertLineMapping(factory, 4, 38, ' *123456789012345678901234567890123456|7890', WrappingIndent.Same); }); test('issue #16332: Scroll bar overlaying on top of text', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', ' '); + let factory = new CharacterHardWrappingLineMapperFactory('', '\t '); assertLineMapping(factory, 4, 24, 'a/ very/long/line/of/tex|t/that/expands/beyon|d/your/typical/line/|of/code/', WrappingIndent.Indent); }); test('issue #35162: wrappingIndent not consistently working', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', ' '); + let factory = new CharacterHardWrappingLineMapperFactory('', '\t '); let mapper = assertLineMapping(factory, 4, 24, ' t h i s |i s |a l |o n |g l |i n |e', WrappingIndent.Indent); assert.equal(mapper!.getWrappedLinesIndent(), ' \t'); }); test('issue #75494: surrogate pairs', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', ' '); + let factory = new CharacterHardWrappingLineMapperFactory('\t', ' '); assertLineMapping(factory, 4, 49, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇|👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬', WrappingIndent.Same); }); test('CharacterHardWrappingLineMapper - WrappingIndent.DeepIndent', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', ' '); + let factory = new CharacterHardWrappingLineMapperFactory('', '\t '); let mapper = assertLineMapping(factory, 4, 26, ' W e A r e T e s t |i n g D e |e p I n d |e n t a t |i o n', WrappingIndent.DeepIndent); assert.equal(mapper!.getWrappedLinesIndent(), ' \t\t'); }); From 2f90debe00823fdce8be1be040610affc20c5b70 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 12:07:37 +0100 Subject: [PATCH 043/843] :lipstick: --- .../characterHardWrappingLineMapper.ts | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 5e8265cf0b6..fad43a06e26 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -153,16 +153,16 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor // but the character at `i` might not fit const charCode = lineText.charCodeAt(i); - const charCodeIsTab = (charCode === CharCode.Tab); - const charCodeClass = classifier.get(charCode); - - if (strings.isLowSurrogate(charCode)/* && i + 1 < len */) { + if (strings.isLowSurrogate(charCode)) { // A surrogate pair must always be considered as a single unit, so it is never to be broken // => advance visibleColumn by 1 and advance to next char code... visibleColumn = visibleColumn + 1; continue; } + const charCodeIsTab = (charCode === CharCode.Tab); + const charCodeClass = classifier.get(charCode); + if (charCodeClass === CharacterClass.BREAK_BEFORE) { // This is a character that indicates that a break should happen before it // Since we are certain the character before `i` fits, there's no extra checking needed, @@ -191,33 +191,27 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor // - otherwise, break before obtrusiveBreakLastOffset if it exists (and re-establish a correct visibleColumn by using obtrusiveBreakVisibleColumn + charAt(i)) // - otherwise, break before i (and re-establish a correct visibleColumn by charAt(i)) - let breakBeforeOffset: number; - let restoreVisibleColumnFrom: number; - if (niceBreakOffset !== -1 && niceBreakVisibleColumn <= breakingColumn) { // We will break before `niceBreakLastOffset` - breakBeforeOffset = niceBreakOffset; - restoreVisibleColumnFrom = niceBreakVisibleColumn; + breakingLengths[breakingLengthsIndex++] = niceBreakOffset - lastBreakingOffset; + lastBreakingOffset = niceBreakOffset; + visibleColumn = niceBreakVisibleColumn; } else { // We will break before `i` - breakBeforeOffset = i; - restoreVisibleColumnFrom = wrappedTextIndentVisibleColumn; + breakingLengths[breakingLengthsIndex++] = i - lastBreakingOffset; + lastBreakingOffset = i; + visibleColumn = wrappedTextIndentVisibleColumn; } - // Break before character at `breakBeforeOffset` - breakingLengths[breakingLengthsIndex++] = breakBeforeOffset - lastBreakingOffset; - lastBreakingOffset = breakBeforeOffset; - // Re-establish visibleColumn by taking character at `i` into account - visibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(restoreVisibleColumnFrom, tabSize, charCodeIsTab, charColumnSize); + visibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(visibleColumn, tabSize, charCodeIsTab, charColumnSize); // Reset markers niceBreakOffset = -1; - niceBreakVisibleColumn = 0; } // At this point, there is a certainty that the character at `i` fits on the current line From 9ddc82f5c6a250bcce97ec9f7e6990e05e9e6dd5 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 12:12:27 +0100 Subject: [PATCH 044/843] Also advance niceBreakVisibleColumn in case of surrogate pairs --- .../characterHardWrappingLineMapper.ts | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index fad43a06e26..207082b55db 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -155,12 +155,17 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor const charCode = lineText.charCodeAt(i); if (strings.isLowSurrogate(charCode)) { // A surrogate pair must always be considered as a single unit, so it is never to be broken - // => advance visibleColumn by 1 and advance to next char code... + + // Advance visibleColumn visibleColumn = visibleColumn + 1; + + if (niceBreakOffset !== -1) { + // Advance niceBreakVisibleColumn + niceBreakVisibleColumn = niceBreakVisibleColumn + 1; + } continue; } - const charCodeIsTab = (charCode === CharCode.Tab); const charCodeClass = classifier.get(charCode); if (charCodeClass === CharacterClass.BREAK_BEFORE) { @@ -172,14 +177,13 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor } // CJK breaking : before break - if (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i > 0) { - const prevClass = classifier.get(lineText.charCodeAt(i - 1)); - if (prevClass !== CharacterClass.BREAK_BEFORE) { // Kinsoku Shori: Don't break after a leading character, like an open bracket - niceBreakOffset = i; - niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; - } + if (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i > 0 && classifier.get(lineText.charCodeAt(i - 1)) !== CharacterClass.BREAK_BEFORE) { + // Kinsoku Shori: Don't break after a leading character, like an open bracket + niceBreakOffset = i; + niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; } + const charCodeIsTab = (charCode === CharCode.Tab); const charColumnSize = strings.isFullWidthCharacter(charCode) ? columnsForFullWidthChar : 1; // Advance visibleColumn with character at `i` @@ -228,12 +232,10 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor } // CJK breaking : after break - if (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i < len - 1) { - const nextClass = classifier.get(lineText.charCodeAt(i + 1)); - if (nextClass !== CharacterClass.BREAK_AFTER) { // Kinsoku Shori: Don't break before a trailing character, like a period - niceBreakOffset = i + 1; - niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; - } + if (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i + 1 < len && classifier.get(lineText.charCodeAt(i + 1)) !== CharacterClass.BREAK_AFTER) { + // Kinsoku Shori: Don't break before a trailing character, like a period + niceBreakOffset = i + 1; + niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; } } From 13660367d4de905d7238e707f1cc6eaab082c992 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 7 Jan 2020 12:18:39 +0100 Subject: [PATCH 045/843] use BulkFileOperation model for tree and preview --- .../contrib/bulkEdit/browser/bulkEditPanel.ts | 23 ++-- .../bulkEdit/browser/bulkEditPreview.ts | 101 ++++++++++++++---- .../contrib/bulkEdit/browser/bulkEditTree.ts | 34 +++--- 3 files changed, 109 insertions(+), 49 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts index aab4b172409..e75ce385d30 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts @@ -19,7 +19,7 @@ import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistr import { localize } from 'vs/nls'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { BulkEditPreviewProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { BulkEditPreviewProvider, BulkFileOperations } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { URI } from 'vs/base/common/uri'; @@ -32,9 +32,8 @@ const enum State { export class BulkEditPanel extends Panel { static readonly ID = 'BulkEditPanel'; - private static EmptyWorkspaceEdit = { edits: [] }; - private _tree!: WorkbenchAsyncDataTree; + private _tree!: WorkbenchAsyncDataTree; private _message!: HTMLSpanElement; private readonly _acceptAction = new Action('ok', localize('ok', "Apply Refactoring"), 'codicon-check', false, async () => this._done(true)); @@ -113,24 +112,26 @@ export class BulkEditPanel extends Panel { this.getContainer()!.dataset['state'] = state; } - setInput(edit: WorkspaceEdit): Promise { + async setInput(edit: WorkspaceEdit): Promise { this._setState(State.Data); this._sessionDisposables.clear(); - this._sessionDisposables.add(this._instaService.createInstance(BulkEditPreviewProvider, edit)); if (this._currentResolve) { this._currentResolve(false); this._currentResolve = undefined; } + this._sessionDisposables.add(this._instaService.createInstance(BulkEditPreviewProvider, edit)); + const input = await this._instaService.invokeFunction(BulkFileOperations.create, edit); + this._acceptAction.enabled = true; this._discardAction.enabled = true; return new Promise(async resolve => { this._currentResolve = resolve; - await this._tree.setInput(edit); + await this._tree.setInput(input); this._tree.domFocus(); this._tree.focusFirst(); @@ -148,7 +149,7 @@ export class BulkEditPanel extends Panel { this._currentResolve(accept); this._acceptAction.enabled = false; this._discardAction.enabled = false; - this._tree.setInput(BulkEditPanel.EmptyWorkspaceEdit); + this._tree.setInput(new BulkFileOperations([])); } } @@ -157,18 +158,18 @@ export class BulkEditPanel extends Panel { let leftResource: URI; try { - (await this._textModelService.createModelReference(edit.parent.resource)).dispose(); - leftResource = edit.parent.resource; + (await this._textModelService.createModelReference(edit.parent.uri)).dispose(); + leftResource = edit.parent.uri; } catch { leftResource = BulkEditPreviewProvider.emptyPreview; } - const previewUri = BulkEditPreviewProvider.asPreviewUri(edit.parent.resource); + const previewUri = BulkEditPreviewProvider.asPreviewUri(edit.parent.uri); this._editorService.openEditor({ leftResource, rightResource: previewUri, - label: localize('edt.title', "{0} (Refactor Preview)", this._labelService.getUriLabel(edit.parent.resource)), + label: localize('edt.title', "{0} (Refactor Preview)", this._labelService.getUriLabel(edit.parent.uri)), options: { selection: edit.edit.range // preserveFocus, diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index 59695cdc2ec..d656d7596aa 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -8,15 +8,95 @@ import { URI } from 'vs/base/common/uri'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; -import { WorkspaceEdit, isResourceTextEdit, isResourceFileEdit } from 'vs/editor/common/modes'; +import { WorkspaceEdit, isResourceTextEdit, isResourceFileEdit, TextEdit } from 'vs/editor/common/modes'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { mergeSort } from 'vs/base/common/arrays'; import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import * as files from 'vs/platform/files/common/files'; import { Event, Emitter } from 'vs/base/common/event'; -import { TernarySearchTree } from 'vs/base/common/map'; +import { TernarySearchTree, values } from 'vs/base/common/map'; import { basename } from 'vs/base/common/resources'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; + +export const enum BulkFileOperationType { + None = 0, + Create = 0b0001, + Delete = 0b0010, + Rename = 0b0100, +} + +export class BulkFileOperation { + + type = BulkFileOperationType.None; + textEdits: TextEdit[] = []; + + constructor(readonly uri: URI) { } + + addType(type: BulkFileOperationType) { + this.type += type; + } + + addEdits(edits: TextEdit[]) { + this.textEdits = this.textEdits.concat(edits); + } +} + +export class BulkFileOperations { + + static async create(_accessor: ServicesAccessor, bulkEdit: WorkspaceEdit): Promise { + + const operationByResource = new Map(); + + for (const edit of bulkEdit.edits) { + + let uri: URI; + let type: BulkFileOperationType; + let textEdits: TextEdit[] | undefined; + + if (isResourceTextEdit(edit)) { + type = BulkFileOperationType.None; + uri = edit.resource; + textEdits = edit.edits; + + } else if (edit.newUri && edit.oldUri) { + type = BulkFileOperationType.Rename; + uri = edit.oldUri; + + } else if (edit.oldUri) { + type = BulkFileOperationType.Delete; + uri = edit.oldUri; + + } else if (edit.newUri) { + type = BulkFileOperationType.Create; + uri = edit.newUri; + + } else { + // invalid edit -> skip + continue; + } + + + const key = uri.toString(); + let operation = operationByResource.get(key); + if (!operation) { + operation = new BulkFileOperation(uri); + operationByResource.set(key, operation); + } + + operation.addType(type); + if (textEdits) { + operation.addEdits(textEdits); + } + } + + //todo@joh filter noops + + return new BulkFileOperations(values(operationByResource)); + } + + constructor(readonly fileOperations: BulkFileOperation[]) { } +} export class BulkEditPreviewProvider implements ITextModelContentProvider { @@ -107,7 +187,6 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider { } } - export function asPreviewUri(uri: URI): URI { return URI.from({ scheme: 'vscode-bulkedit-preview', path: uri.path, query: uri.toString() }); } @@ -116,22 +195,6 @@ export function fromPreviewUri(uri: URI): URI { return URI.parse(uri.query); } -export function asPreviewEdit(edit: WorkspaceEdit): WorkspaceEdit { - const result: WorkspaceEdit = { edits: [] }; - for (let child of edit.edits) { - if (isResourceTextEdit(child)) { - result.edits.push({ ...child, resource: asPreviewUri(child.resource) }); - } else { - result.edits.push({ - oldUri: child.oldUri && asPreviewUri(child.oldUri), - newUri: child.newUri && asPreviewUri(child.newUri), - options: child.options - }); - } - } - return result; -} - class File implements files.IStat { type: files.FileType; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 321eb64bf45..9b0eb46c031 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -18,6 +18,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { IDisposable } from 'vs/base/common/lifecycle'; import { TextModel } from 'vs/editor/common/model/textModel'; import { ILabelService } from 'vs/platform/label/common/label'; +import { BulkFileOperations, BulkFileOperation } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; // --- VIEW MODEL @@ -26,14 +27,9 @@ export class FileElement { readonly uri: URI; readonly _debugName: string; - constructor(readonly edit: modes.ResourceFileEdit | modes.ResourceTextEdit) { - this.uri = modes.isResourceTextEdit(this.edit) - ? this.edit.resource - : this.edit.oldUri || this.edit.newUri!; - - this._debugName = modes.isResourceTextEdit(this.edit) - ? 'text edit' - : this.edit.oldUri && this.edit.newUri ? 'rename' : this.edit.newUri ? 'create' : 'delete'; + constructor(readonly edit: BulkFileOperation) { + this.uri = edit.uri; + this._debugName = `0b${edit.type.toString(2)}`; } } @@ -41,7 +37,7 @@ export class FileElement { export class TextEditElement { constructor( - readonly parent: modes.ResourceTextEdit, + readonly parent: FileElement, readonly edit: modes.TextEdit, readonly prefix: string, readonly selecting: string, readonly inserting: string, readonly suffix: string ) { } @@ -51,13 +47,13 @@ export type Edit = FileElement | TextEditElement; // --- DATA SOURCE -export class BulkEditDataSource implements IAsyncDataSource { +export class BulkEditDataSource implements IAsyncDataSource { constructor(@ITextModelService private readonly _textModelService: ITextModelService) { } - hasChildren(element: modes.WorkspaceEdit | Edit): boolean { + hasChildren(element: BulkFileOperations | Edit): boolean { if (element instanceof FileElement) { - return modes.isResourceTextEdit(element.edit); + return element.edit.textEdits.length > 0; } if (element instanceof TextEditElement) { return false; @@ -65,20 +61,20 @@ export class BulkEditDataSource implements IAsyncDataSource { + async getChildren(element: BulkFileOperations | Edit): Promise { // root -> file/text edits - if (Array.isArray((element).edits)) { - return (element).edits.map(edit => new FileElement(edit)); + if (element instanceof BulkFileOperations) { + return element.fileOperations.map(op => new FileElement(op)); } // file: text edit - if (element instanceof FileElement && modes.isResourceTextEdit(element.edit)) { + if (element instanceof FileElement && element.edit.textEdits.length > 0) { // const previewUri = BulkEditPreviewProvider.asPreviewUri(element.edit.resource); let textModel: ITextModel; let textModelDisposable: IDisposable; try { - const ref = await this._textModelService.createModelReference(element.edit.resource); + const ref = await this._textModelService.createModelReference(element.edit.uri); textModel = ref.object.textEditorModel; textModelDisposable = ref; } catch { @@ -86,7 +82,7 @@ export class BulkEditDataSource implements IAsyncDataSource { + const result = element.edit.textEdits.map(edit => { const range = Range.lift(edit.range); const tokens = textModel.getLineTokens(range.endLineNumber); @@ -96,7 +92,7 @@ export class BulkEditDataSource implements IAsyncDataSourceelement.edit, + element, edit, textModel.getValueInRange(new Range(range.startLineNumber, 1, range.startLineNumber, range.startColumn)), // line start to edit start, textModel.getValueInRange(range), From 8c8e320238cc0cc8f84d68422eb9bb7dd46f7bd1 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 12:44:25 +0100 Subject: [PATCH 046/843] simplify --- .../characterHardWrappingLineMapper.ts | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 207082b55db..714d397d6d8 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -144,7 +144,7 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor let breakingLengths: number[] = []; // The length of each broken-up line text let breakingLengthsIndex: number = 0; // The count of breaks already done let visibleColumn = 0; // Visible column since the beginning of the current line - let niceBreakOffset = -1; // Last index of a character that indicates a break should happen before it (more desirable) + let niceBreakOffset = 0; // Last index of a character that indicates a break should happen before it (more desirable) let niceBreakVisibleColumn = 0; // visible column if a break were to be later introduced before `niceBreakOffset` const len = lineText.length; @@ -156,11 +156,9 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor if (strings.isLowSurrogate(charCode)) { // A surrogate pair must always be considered as a single unit, so it is never to be broken - // Advance visibleColumn + // Advance visibleColumn & niceBreakVisibleColumn visibleColumn = visibleColumn + 1; - - if (niceBreakOffset !== -1) { - // Advance niceBreakVisibleColumn + if (niceBreakOffset !== 0) { niceBreakVisibleColumn = niceBreakVisibleColumn + 1; } continue; @@ -168,21 +166,18 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor const charCodeClass = classifier.get(charCode); - if (charCodeClass === CharacterClass.BREAK_BEFORE) { + if ( + (charCodeClass === CharacterClass.BREAK_BEFORE) + || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i > 0 && classifier.get(lineText.charCodeAt(i - 1)) !== CharacterClass.BREAK_BEFORE) + ) { // This is a character that indicates that a break should happen before it + // (or) CJK breaking : before break : Kinsoku Shori : Don't break after a leading character, like an open bracket // Since we are certain the character before `i` fits, there's no extra checking needed, // just mark it as a nice breaking opportunity niceBreakOffset = i; niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; } - // CJK breaking : before break - if (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i > 0 && classifier.get(lineText.charCodeAt(i - 1)) !== CharacterClass.BREAK_BEFORE) { - // Kinsoku Shori: Don't break after a leading character, like an open bracket - niceBreakOffset = i; - niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; - } - const charCodeIsTab = (charCode === CharCode.Tab); const charColumnSize = strings.isFullWidthCharacter(charCode) ? columnsForFullWidthChar : 1; @@ -195,7 +190,7 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor // - otherwise, break before obtrusiveBreakLastOffset if it exists (and re-establish a correct visibleColumn by using obtrusiveBreakVisibleColumn + charAt(i)) // - otherwise, break before i (and re-establish a correct visibleColumn by charAt(i)) - if (niceBreakOffset !== -1 && niceBreakVisibleColumn <= breakingColumn) { + if (niceBreakOffset !== 0 && niceBreakVisibleColumn <= breakingColumn) { // We will break before `niceBreakLastOffset` breakingLengths[breakingLengthsIndex++] = niceBreakOffset - lastBreakingOffset; @@ -215,25 +210,22 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor visibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(visibleColumn, tabSize, charCodeIsTab, charColumnSize); // Reset markers - niceBreakOffset = -1; + niceBreakOffset = 0; } // At this point, there is a certainty that the character at `i` fits on the current line - if (niceBreakOffset !== -1) { + if (niceBreakOffset !== 0) { // Advance niceBreakVisibleColumn niceBreakVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(niceBreakVisibleColumn, tabSize, charCodeIsTab, charColumnSize); } - if (charCodeClass === CharacterClass.BREAK_AFTER && (hardWrappingIndent === WrappingIndent.None || i >= firstNonWhitespaceIndex)) { + if ( + (charCodeClass === CharacterClass.BREAK_AFTER && (hardWrappingIndent === WrappingIndent.None || i >= firstNonWhitespaceIndex)) + || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i + 1 < len && classifier.get(lineText.charCodeAt(i + 1)) !== CharacterClass.BREAK_AFTER) + ) { // This is a character that indicates that a break should happen after it - niceBreakOffset = i + 1; - niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; - } - - // CJK breaking : after break - if (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i + 1 < len && classifier.get(lineText.charCodeAt(i + 1)) !== CharacterClass.BREAK_AFTER) { - // Kinsoku Shori: Don't break before a trailing character, like a period + // (or) CJK breaking : after break : Kinsoku Shori : Don't break before a trailing character, like a period niceBreakOffset = i + 1; niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; } From d5a5421bbfd30c2d5a6b06e14808a68e74a85d93 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 7 Jan 2020 12:54:04 +0100 Subject: [PATCH 047/843] some cleanup --- .../bulkEdit/browser/bulkEditService.ts | 33 +++---------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 08390b52432..7d543daaadd 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -404,37 +404,14 @@ export class BulkEditService implements IBulkEditService { }); } - private static _mergeSequentialTextEditsOfSameResource(workspaceEdit: WorkspaceEdit): WorkspaceEdit { - let newEdit: WorkspaceEdit = { edits: [] }; - let last: ResourceTextEdit | undefined; - for (let edit of workspaceEdit.edits) { - if (!isResourceTextEdit(edit)) { - last = undefined; - newEdit.edits.push(edit); + async apply(edit: WorkspaceEdit, options?: IBulkEditOptions): Promise { - } else { - if (!last || last.resource.toString() !== edit.resource.toString()) { - last = edit; - newEdit.edits.push(last); - } else { - last.edits.push(...edit.edits); - last.modelVersionId = last.modelVersionId || edit.modelVersionId; - } - } - } - return newEdit; - } - - async apply(edit: WorkspaceEdit, options: IBulkEditOptions = {}): Promise { - - edit = BulkEditService._mergeSequentialTextEditsOfSameResource(edit); - - if (this._previewHandler && !options.noPreview) { + if (this._previewHandler && !options?.noPreview) { edit = await this._previewHandler(edit, options); } - let { edits } = edit; - let codeEditor = options.editor; + const { edits } = edit; + let codeEditor = options?.editor; // First check if loaded models were not changed in the meantime for (const edit of edits) { @@ -461,7 +438,7 @@ export class BulkEditService implements IBulkEditService { codeEditor = undefined; } const bulkEdit = new BulkEdit( - codeEditor, options.progress, edits, + codeEditor, options?.progress, edits, this._logService, this._textModelService, this._fileService, this._workerService, this._textFileService, this._labelService, this._configurationService ); return bulkEdit.perform().then(() => { From 3892a7d05fc83062046a3941a860e26428b1e938 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 7 Jan 2020 12:54:15 +0100 Subject: [PATCH 048/843] only preview rename and code actions --- src/vs/editor/contrib/rename/rename.ts | 20 +++++++++++-------- .../api/browser/mainThreadEditors.ts | 2 +- .../contrib/search/browser/replaceService.ts | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 92fccb848b4..f85e8d9d80e 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -30,6 +30,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { DisposableStore } from 'vs/base/common/lifecycle'; import { IdleValue, raceCancellation } from 'vs/base/common/async'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { ILogService } from 'vs/platform/log/common/log'; class RenameSkeleton { @@ -114,6 +115,7 @@ class RenameController implements IEditorContribution { @IEditorProgressService private readonly _progressService: IEditorProgressService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IThemeService private readonly _themeService: IThemeService, + @ILogService private readonly _logService: ILogService, ) { this._renameInputField = this._dispoableStore.add(new IdleValue(() => this._dispoableStore.add(new RenameInputField(this.editor, this._themeService, this._contextKeyService)))); } @@ -197,16 +199,18 @@ class RenameController implements IEditorContribution { return; } - const editResult = await this._bulkEditService.apply(renameResult, { editor: this.editor }); - - // alert - if (editResult.ariaSummary) { - alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, newNameOrFocusFlag, editResult.ariaSummary)); - } + this._bulkEditService.apply(renameResult, { editor: this.editor }).then(result => { + if (result.ariaSummary) { + alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, newNameOrFocusFlag, result.ariaSummary)); + } + }).catch(err => { + this._notificationService.error(nls.localize('rename.failedApply', "Rename failed to apply edits")); + this._logService.error(err); + }); }, err => { - this._notificationService.error(nls.localize('rename.failed', "Rename failed to execute.")); - return Promise.reject(err); + this._notificationService.error(nls.localize('rename.failed', "Rename failed to compute edits")); + this._logService.error(err); }); this._progressService.showWhile(renameOperation, 250); diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index c4658d50a69..8ac224f6de0 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -217,7 +217,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { $tryApplyWorkspaceEdit(dto: IWorkspaceEditDto): Promise { const { edits } = reviveWorkspaceEditDto(dto); - return this._bulkEditService.apply({ edits }, undefined).then(() => true, err => false); + return this._bulkEditService.apply({ edits }, { noPreview: true }).then(() => true, err => false); } $tryInsertSnippet(id: string, template: string, ranges: readonly IRange[], opts: IUndoStopOptions): Promise { diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index bc7e14ab2e2..92a9cf4b56b 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -105,7 +105,7 @@ export class ReplaceService implements IReplaceService { replace(match: FileMatchOrMatch, progress?: IProgress, resource?: URI): Promise; replace(arg: any, progress: IProgress | undefined = undefined, resource: URI | null = null): Promise { const edits: ResourceTextEdit[] = this.createEdits(arg, resource); - return this.bulkEditorService.apply({ edits }, { progress }).then(() => this.textFileService.saveAll(edits.map(e => e.resource))); + return this.bulkEditorService.apply({ edits }, { progress, noPreview: true }).then(() => this.textFileService.saveAll(edits.map(e => e.resource))); } openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { From 1b573d75296630e87c914e778e6a67691b4a050c Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 13:47:43 +0100 Subject: [PATCH 049/843] Avoid GCing (#21196) --- .../characterHardWrappingLineMapper.ts | 59 ++++++++++++------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 714d397d6d8..937cddc0a99 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -7,8 +7,6 @@ import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { WrappingIndent, IComputedEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; -import { toUint32Array } from 'vs/base/common/uint'; -import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; import { ILineMapperFactory, ILineMapping, OutputPosition, ILineMappingComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; const enum CharacterClass { @@ -140,9 +138,8 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor } const classifier = this.classifier; - let lastBreakingOffset = 0; // Last 0-based offset in the lineText at which a break happened - let breakingLengths: number[] = []; // The length of each broken-up line text - let breakingLengthsIndex: number = 0; // The count of breaks already done + let breakingOffsets: number[] = []; // The offset where a break occurs + let breakingOffsetsIndex: number = 0; // The count of breaks already done let visibleColumn = 0; // Visible column since the beginning of the current line let niceBreakOffset = 0; // Last index of a character that indicates a break should happen before it (more desirable) let niceBreakVisibleColumn = 0; // visible column if a break were to be later introduced before `niceBreakOffset` @@ -193,15 +190,13 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor if (niceBreakOffset !== 0 && niceBreakVisibleColumn <= breakingColumn) { // We will break before `niceBreakLastOffset` - breakingLengths[breakingLengthsIndex++] = niceBreakOffset - lastBreakingOffset; - lastBreakingOffset = niceBreakOffset; + breakingOffsets[breakingOffsetsIndex++] = niceBreakOffset; visibleColumn = niceBreakVisibleColumn; } else { // We will break before `i` - breakingLengths[breakingLengthsIndex++] = i - lastBreakingOffset; - lastBreakingOffset = i; + breakingOffsets[breakingOffsetsIndex++] = i; visibleColumn = wrappedTextIndentVisibleColumn; } @@ -231,32 +226,29 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor } } - if (breakingLengthsIndex === 0) { + if (breakingOffsetsIndex === 0) { return null; } // Add last segment - breakingLengths[breakingLengthsIndex++] = len - lastBreakingOffset; + breakingOffsets[breakingOffsetsIndex++] = len; - return new CharacterHardWrappingLineMapping( - new PrefixSumComputer(toUint32Array(breakingLengths)), - wrappedTextIndent - ); + return new CharacterHardWrappingLineMapping(breakingOffsets, wrappedTextIndent); } } export class CharacterHardWrappingLineMapping implements ILineMapping { - private readonly _prefixSums: PrefixSumComputer; + private readonly _breakingOffsets: number[]; private readonly _wrappedLinesIndent: string; - constructor(prefixSums: PrefixSumComputer, wrappedLinesIndent: string) { - this._prefixSums = prefixSums; + constructor(breakingOffsets: number[], wrappedLinesIndent: string) { + this._breakingOffsets = breakingOffsets; this._wrappedLinesIndent = wrappedLinesIndent; } public getOutputLineCount(): number { - return this._prefixSums.getCount(); + return this._breakingOffsets.length; } public getWrappedLinesIndent(): string { @@ -267,12 +259,35 @@ export class CharacterHardWrappingLineMapping implements ILineMapping { if (outputLineIndex === 0) { return outputOffset; } else { - return this._prefixSums.getAccumulatedValue(outputLineIndex - 1) + outputOffset; + return this._breakingOffsets[outputLineIndex - 1] + outputOffset; } } public getOutputPositionOfInputOffset(inputOffset: number): OutputPosition { - let r = this._prefixSums.getIndexOf(inputOffset); - return new OutputPosition(r.index, r.remainder); + inputOffset = inputOffset | 0; //@perf + const breakingOffsets = this._breakingOffsets; + + let low = 0; + let high = breakingOffsets.length - 1; + let mid = 0; + let midStop = 0; + let midStart = 0; + + while (low <= high) { + mid = low + ((high - low) / 2) | 0; + + midStop = breakingOffsets[mid]; + midStart = mid > 0 ? breakingOffsets[mid - 1] : 0; + + if (inputOffset < midStart) { + high = mid - 1; + } else if (inputOffset >= midStop) { + low = mid + 1; + } else { + break; + } + } + + return new OutputPosition(mid, inputOffset - midStart); } } From 3fbc4687b2569b1f14aad14dcede350d770851f0 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 13:51:25 +0100 Subject: [PATCH 050/843] Fix compilation errors --- src/vs/base/common/uint.ts | 9 --------- .../test/common/viewModel/prefixSumComputer.test.ts | 11 ++++++++++- .../common/viewModel/splitLinesCollection.test.ts | 11 +++++------ 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/vs/base/common/uint.ts b/src/vs/base/common/uint.ts index b44e0fdaec7..347af57eec2 100644 --- a/src/vs/base/common/uint.ts +++ b/src/vs/base/common/uint.ts @@ -57,12 +57,3 @@ export function toUint32(v: number): number { } return v | 0; } - -export function toUint32Array(arr: number[]): Uint32Array { - const len = arr.length; - const r = new Uint32Array(len); - for (let i = 0; i < len; i++) { - r[i] = toUint32(arr[i]); - } - return r; -} diff --git a/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts b/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts index 313c672b101..65cc26efd4c 100644 --- a/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts +++ b/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts @@ -4,9 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { toUint32Array } from 'vs/base/common/uint'; +import { toUint32 } from 'vs/base/common/uint'; import { PrefixSumComputer, PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer'; +function toUint32Array(arr: number[]): Uint32Array { + const len = arr.length; + const r = new Uint32Array(len); + for (let i = 0; i < len; i++) { + r[i] = toUint32(arr[i]); + } + return r; +} + suite('Editor ViewModel - PrefixSumComputer', () => { test('PrefixSumComputer', () => { diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index be941b900e1..833d0d7313b 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -9,13 +9,11 @@ import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; -import { toUint32Array } from 'vs/base/common/uint'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import * as modes from 'vs/editor/common/modes'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; import { CharacterHardWrappingLineMapperFactory, CharacterHardWrappingLineMapping } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; -import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; import { ILineMapping, ISimpleModel, SplitLine, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; @@ -779,10 +777,11 @@ function createSplitLine(splitLengths: number[], wrappedLinesPrefix: string, isV } function createLineMapping(breakingLengths: number[], wrappedLinesPrefix: string): ILineMapping { - return new CharacterHardWrappingLineMapping( - new PrefixSumComputer(toUint32Array(breakingLengths)), - wrappedLinesPrefix - ); + let sums: number[] = []; + for (let i = 0; i < breakingLengths.length; i++) { + sums[i] = (i > 0 ? sums[i - 1] : 0) + breakingLengths[i]; + } + return new CharacterHardWrappingLineMapping(sums, wrappedLinesPrefix); } function createModel(text: string): ISimpleModel { From b8a1bd17bff98a76678b64c2d37bb100e909d684 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 7 Jan 2020 13:57:09 +0100 Subject: [PATCH 051/843] use file operations for preview provider, make sure preview provider keeps models alive --- .../contrib/bulkEdit/browser/bulkEditPanel.ts | 2 +- .../bulkEdit/browser/bulkEditPreview.ts | 32 ++++++++----------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts index e75ce385d30..82a1eb77df1 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts @@ -122,8 +122,8 @@ export class BulkEditPanel extends Panel { this._currentResolve = undefined; } - this._sessionDisposables.add(this._instaService.createInstance(BulkEditPreviewProvider, edit)); const input = await this._instaService.invokeFunction(BulkFileOperations.create, edit); + this._sessionDisposables.add(this._instaService.createInstance(BulkEditPreviewProvider, input)); this._acceptAction.enabled = true; this._discardAction.enabled = true; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index d656d7596aa..aee181049b0 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; -import { WorkspaceEdit, isResourceTextEdit, isResourceFileEdit, TextEdit } from 'vs/editor/common/modes'; +import { WorkspaceEdit, isResourceTextEdit, TextEdit } from 'vs/editor/common/modes'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { mergeSort } from 'vs/base/common/arrays'; import { Range } from 'vs/editor/common/core/range'; @@ -116,7 +116,7 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider { private readonly _ready: Promise; constructor( - private readonly _edit: WorkspaceEdit, + private readonly _operations: BulkFileOperations, @IModeService private readonly _modeService: IModeService, @IModelService private readonly _modelService: IModelService, @ITextModelService private readonly _textModelResolverService: ITextModelService @@ -154,27 +154,21 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider { previewUri ); } - this._disposables.add(model); } + // this is a little weird but otherwise editors and other cusomers + // will dispose my models before they should be disposed... + // And all of this is off the eventloop to prevent endless recursion + new Promise(async () => this._disposables.add(await this._textModelResolverService.createModelReference(model!.uri))); return model; }; - for (let edit of this._edit.edits) { - if (isResourceFileEdit(edit)) { - if (URI.isUri(edit.newUri)) { - await getOrCreatePreviewModel(edit.newUri); - } - if (URI.isUri(edit.oldUri)) { - await getOrCreatePreviewModel(edit.oldUri); - } - } else { - const model = await getOrCreatePreviewModel(edit.resource); - const editOperations = mergeSort( - edit.edits.map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text)), - (a, b) => Range.compareRangesUsingStarts(a.range, b.range) - ); - model.applyEdits(editOperations); - } + for (let operation of this._operations.fileOperations) { + const model = await getOrCreatePreviewModel(operation.uri); + const editOperations = mergeSort( + operation.textEdits.map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text)), + (a, b) => Range.compareRangesUsingStarts(a.range, b.range) + ); + model.applyEdits(editOperations); } } From 3cdfbd5fb19ede14a5793ec5c0b8f5eee090572b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 7 Jan 2020 14:32:16 +0100 Subject: [PATCH 052/843] (re)enable preview for vscode.workspace.applyWorkspaceEdit --- src/vs/workbench/api/browser/mainThreadEditors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index 8ac224f6de0..421201a9c64 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -217,7 +217,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { $tryApplyWorkspaceEdit(dto: IWorkspaceEditDto): Promise { const { edits } = reviveWorkspaceEditDto(dto); - return this._bulkEditService.apply({ edits }, { noPreview: true }).then(() => true, err => false); + return this._bulkEditService.apply({ edits }, { noPreview: false }).then(() => true, err => false); } $tryInsertSnippet(id: string, template: string, ranges: readonly IRange[], opts: IUndoStopOptions): Promise { From faea8dc0be559420e477ce0b85402c2afa876172 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 15:01:31 +0100 Subject: [PATCH 053/843] Reduce memory usage for SplitLines --- .../characterHardWrappingLineMapper.ts | 63 +---------- .../common/viewModel/splitLinesCollection.ts | 107 +++++++++++------- .../characterHardWrappingLineMapper.test.ts | 10 +- .../viewModel/splitLinesCollection.test.ts | 8 +- 4 files changed, 80 insertions(+), 108 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 937cddc0a99..5f4bc7f1828 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -7,7 +7,7 @@ import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { WrappingIndent, IComputedEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; -import { ILineMapperFactory, ILineMapping, OutputPosition, ILineMappingComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { ILineMapperFactory, LineBreakingData, ILineMappingComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; const enum CharacterClass { NONE = 0, @@ -85,7 +85,7 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor requests.push(lineText); }, finalize: () => { - let result: (ILineMapping | null)[] = []; + let result: (LineBreakingData | null)[] = []; for (let i = 0, len = requests.length; i < len; i++) { result[i] = this._createLineMapping(requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); } @@ -94,7 +94,7 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor }; } - private _createLineMapping(lineText: string, tabSize: number, breakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): ILineMapping | null { + private _createLineMapping(lineText: string, tabSize: number, breakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { if (breakingColumn === -1) { return null; } @@ -233,61 +233,6 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor // Add last segment breakingOffsets[breakingOffsetsIndex++] = len; - return new CharacterHardWrappingLineMapping(breakingOffsets, wrappedTextIndent); - } -} - -export class CharacterHardWrappingLineMapping implements ILineMapping { - - private readonly _breakingOffsets: number[]; - private readonly _wrappedLinesIndent: string; - - constructor(breakingOffsets: number[], wrappedLinesIndent: string) { - this._breakingOffsets = breakingOffsets; - this._wrappedLinesIndent = wrappedLinesIndent; - } - - public getOutputLineCount(): number { - return this._breakingOffsets.length; - } - - public getWrappedLinesIndent(): string { - return this._wrappedLinesIndent; - } - - public getInputOffsetOfOutputPosition(outputLineIndex: number, outputOffset: number): number { - if (outputLineIndex === 0) { - return outputOffset; - } else { - return this._breakingOffsets[outputLineIndex - 1] + outputOffset; - } - } - - public getOutputPositionOfInputOffset(inputOffset: number): OutputPosition { - inputOffset = inputOffset | 0; //@perf - const breakingOffsets = this._breakingOffsets; - - let low = 0; - let high = breakingOffsets.length - 1; - let mid = 0; - let midStop = 0; - let midStart = 0; - - while (low <= high) { - mid = low + ((high - low) / 2) | 0; - - midStop = breakingOffsets[mid]; - midStart = mid > 0 ? breakingOffsets[mid - 1] : 0; - - if (inputOffset < midStart) { - high = mid - 1; - } else if (inputOffset >= midStop) { - low = mid + 1; - } else { - break; - } - } - - return new OutputPosition(mid, inputOffset - midStart); + return new LineBreakingData(breakingOffsets, wrappedTextIndent); } } diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 833aabad313..1b64f46d59e 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -16,7 +16,6 @@ import { ITheme } from 'vs/platform/theme/common/themeService'; import { IDisposable } from 'vs/base/common/lifecycle'; export class OutputPosition { - _outputPositionBrand: void; outputLineIndex: number; outputOffset: number; @@ -26,16 +25,49 @@ export class OutputPosition { } } -export interface ILineMapping { - getOutputLineCount(): number; - getWrappedLinesIndent(): string; - getInputOffsetOfOutputPosition(outputLineIndex: number, outputOffset: number): number; - getOutputPositionOfInputOffset(inputOffset: number): OutputPosition; +export class LineBreakingData { + constructor( + public readonly breakOffsets: number[], + public readonly wrappedLinesIndent: string + ) { } + + public static getInputOffsetOfOutputPosition(breakOffsets: number[], outputLineIndex: number, outputOffset: number): number { + if (outputLineIndex === 0) { + return outputOffset; + } else { + return breakOffsets[outputLineIndex - 1] + outputOffset; + } + } + + public static getOutputPositionOfInputOffset(breakOffsets: number[], inputOffset: number): OutputPosition { + let low = 0; + let high = breakOffsets.length - 1; + let mid = 0; + let midStop = 0; + let midStart = 0; + + while (low <= high) { + mid = low + ((high - low) / 2) | 0; + + midStop = breakOffsets[mid]; + midStart = mid > 0 ? breakOffsets[mid - 1] : 0; + + if (inputOffset < midStart) { + high = mid - 1; + } else if (inputOffset >= midStop) { + low = mid + 1; + } else { + break; + } + } + + return new OutputPosition(mid, inputOffset - midStart); + } } export interface ILineMappingComputer { addRequest(lineText: string): void; - finalize(): (ILineMapping | null)[]; + finalize(): (LineBreakingData | null)[]; } export interface ILineMapperFactory { @@ -79,8 +111,8 @@ export interface IViewModelLinesCollection extends IDisposable { createLineMappingComputer(): ILineMappingComputer; onModelFlushed(): void; onModelLinesDeleted(versionId: number, fromLineNumber: number, toLineNumber: number): viewEvents.ViewLinesDeletedEvent | null; - onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, linesMappings: (ILineMapping | null)[]): viewEvents.ViewLinesInsertedEvent | null; - onModelLineChanged(versionId: number, lineNumber: number, lineMapping: ILineMapping | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null]; + onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, linesMappings: (LineBreakingData | null)[]): viewEvents.ViewLinesInsertedEvent | null; + onModelLineChanged(versionId: number, lineNumber: number, lineMapping: LineBreakingData | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null]; acceptVersionId(versionId: number): void; getViewLineCount(): number; @@ -406,7 +438,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return new viewEvents.ViewLinesDeletedEvent(outputFromLineNumber, outputToLineNumber); } - public onModelLinesInserted(versionId: number, fromLineNumber: number, _toLineNumber: number, linesMappings: (ILineMapping | null)[]): viewEvents.ViewLinesInsertedEvent | null { + public onModelLinesInserted(versionId: number, fromLineNumber: number, _toLineNumber: number, linesMappings: (LineBreakingData | null)[]): viewEvents.ViewLinesInsertedEvent | null { if (versionId <= this._validModelVersionId) { // Here we check for versionId in case the lines were reconstructed in the meantime. // We don't want to apply stale change events on top of a newer read model state. @@ -446,7 +478,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return new viewEvents.ViewLinesInsertedEvent(outputFromLineNumber, outputFromLineNumber + totalOutputLineCount - 1); } - public onModelLineChanged(versionId: number, lineNumber: number, lineMapping: ILineMapping | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { + public onModelLineChanged(versionId: number, lineNumber: number, lineMapping: LineBreakingData | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { if (versionId <= this._validModelVersionId) { // Here we check for versionId in case the lines were reconstructed in the meantime. // We don't want to apply stale change events on top of a newer read model state. @@ -1027,18 +1059,13 @@ class InvisibleIdentitySplitLine implements ISplitLine { export class SplitLine implements ISplitLine { - private readonly positionMapper: ILineMapping; - private readonly outputLineCount: number; - - private readonly wrappedIndent: string; - private readonly wrappedIndentLength: number; + private readonly _breakOffsets: number[]; + private readonly _wrappedIndent: string; private _isVisible: boolean; - constructor(positionMapper: ILineMapping, isVisible: boolean) { - this.positionMapper = positionMapper; - this.wrappedIndent = this.positionMapper.getWrappedLinesIndent(); - this.wrappedIndentLength = this.wrappedIndent.length; - this.outputLineCount = this.positionMapper.getOutputLineCount(); + constructor(lineBreaking: LineBreakingData, isVisible: boolean) { + this._breakOffsets = lineBreaking.breakOffsets; + this._wrappedIndent = lineBreaking.wrappedLinesIndent; this._isVisible = isVisible; } @@ -1055,18 +1082,18 @@ export class SplitLine implements ISplitLine { if (!this._isVisible) { return 0; } - return this.outputLineCount; + return this._breakOffsets.length; } private getInputStartOffsetOfOutputLineIndex(outputLineIndex: number): number { - return this.positionMapper.getInputOffsetOfOutputPosition(outputLineIndex, 0); + return LineBreakingData.getInputOffsetOfOutputPosition(this._breakOffsets, outputLineIndex, 0); } private getInputEndOffsetOfOutputLineIndex(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): number { - if (outputLineIndex + 1 === this.outputLineCount) { + if (outputLineIndex + 1 === this._breakOffsets.length) { return model.getLineMaxColumn(modelLineNumber) - 1; } - return this.positionMapper.getInputOffsetOfOutputPosition(outputLineIndex + 1, 0); + return LineBreakingData.getInputOffsetOfOutputPosition(this._breakOffsets, outputLineIndex + 1, 0); } public getViewLineContent(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): string { @@ -1083,7 +1110,7 @@ export class SplitLine implements ISplitLine { }); if (outputLineIndex > 0) { - r = this.wrappedIndent + r; + r = this._wrappedIndent + r; } return r; @@ -1098,7 +1125,7 @@ export class SplitLine implements ISplitLine { let r = endOffset - startOffset; if (outputLineIndex > 0) { - r = this.wrappedIndent.length + r; + r = this._wrappedIndent.length + r; } return r; @@ -1109,7 +1136,7 @@ export class SplitLine implements ISplitLine { throw new Error('Not supported'); } if (outputLineIndex > 0) { - return this.wrappedIndentLength + 1; + return this._wrappedIndent.length + 1; } return 1; } @@ -1137,17 +1164,17 @@ export class SplitLine implements ISplitLine { }); if (outputLineIndex > 0) { - lineContent = this.wrappedIndent + lineContent; + lineContent = this._wrappedIndent + lineContent; } - let minColumn = (outputLineIndex > 0 ? this.wrappedIndentLength + 1 : 1); + let minColumn = (outputLineIndex > 0 ? this._wrappedIndent.length + 1 : 1); let maxColumn = lineContent.length + 1; let continuesWithWrappedLine = (outputLineIndex + 1 < this.getViewLineCount()); let deltaStartIndex = 0; if (outputLineIndex > 0) { - deltaStartIndex = this.wrappedIndentLength; + deltaStartIndex = this._wrappedIndent.length; } let lineTokens = model.getLineTokens(modelLineNumber); @@ -1181,25 +1208,25 @@ export class SplitLine implements ISplitLine { } let adjustedColumn = outputColumn - 1; if (outputLineIndex > 0) { - if (adjustedColumn < this.wrappedIndentLength) { + if (adjustedColumn < this._wrappedIndent.length) { adjustedColumn = 0; } else { - adjustedColumn -= this.wrappedIndentLength; + adjustedColumn -= this._wrappedIndent.length; } } - return this.positionMapper.getInputOffsetOfOutputPosition(outputLineIndex, adjustedColumn) + 1; + return LineBreakingData.getInputOffsetOfOutputPosition(this._breakOffsets, outputLineIndex, adjustedColumn) + 1; } public getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position { if (!this._isVisible) { throw new Error('Not supported'); } - let r = this.positionMapper.getOutputPositionOfInputOffset(inputColumn - 1); + let r = LineBreakingData.getOutputPositionOfInputOffset(this._breakOffsets, inputColumn - 1); let outputLineIndex = r.outputLineIndex; let outputColumn = r.outputOffset + 1; if (outputLineIndex > 0) { - outputColumn += this.wrappedIndentLength; + outputColumn += this._wrappedIndent.length; } // console.log('in -> out ' + deltaLineNumber + ',' + inputColumn + ' ===> ' + (deltaLineNumber+outputLineIndex) + ',' + outputColumn); @@ -1210,12 +1237,12 @@ export class SplitLine implements ISplitLine { if (!this._isVisible) { throw new Error('Not supported'); } - const r = this.positionMapper.getOutputPositionOfInputOffset(inputColumn - 1); + const r = LineBreakingData.getOutputPositionOfInputOffset(this._breakOffsets, inputColumn - 1); return (deltaLineNumber + r.outputLineIndex); } } -function createSplitLine(lineMapping: ILineMapping | null, isVisible: boolean): ISplitLine { +function createSplitLine(lineMapping: LineBreakingData | null, isVisible: boolean): ISplitLine { if (lineMapping === null) { // No mapping needed if (isVisible) { @@ -1332,11 +1359,11 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return new viewEvents.ViewLinesDeletedEvent(fromLineNumber, toLineNumber); } - public onModelLinesInserted(_versionId: number, fromLineNumber: number, toLineNumber: number, linesMappings: (ILineMapping | null)[]): viewEvents.ViewLinesInsertedEvent | null { + public onModelLinesInserted(_versionId: number, fromLineNumber: number, toLineNumber: number, linesMappings: (LineBreakingData | null)[]): viewEvents.ViewLinesInsertedEvent | null { return new viewEvents.ViewLinesInsertedEvent(fromLineNumber, toLineNumber); } - public onModelLineChanged(_versionId: number, lineNumber: number, lineMapping: ILineMapping | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { + public onModelLineChanged(_versionId: number, lineNumber: number, lineMapping: LineBreakingData | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { return [false, new viewEvents.ViewLinesChangedEvent(lineNumber, lineNumber), null, null]; } diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts index ac4c75c2dae..88c4fe05bc5 100644 --- a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts +++ b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts @@ -5,9 +5,9 @@ import * as assert from 'assert'; import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; -import { ILineMapperFactory, ILineMapping } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { ILineMapperFactory, LineBreakingData } from 'vs/editor/common/viewModel/splitLinesCollection'; -function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None): ILineMapping | null { +function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None): LineBreakingData | null { // Create version of `annotatedText` with line break markers removed let rawText = ''; let currentLineIndex = 0; @@ -31,7 +31,7 @@ function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAf if (mapper) { let previousLineIndex = 0; for (let i = 0, len = rawText.length; i < len; i++) { - let r = mapper.getOutputPositionOfInputOffset(i); + let r = LineBreakingData.getOutputPositionOfInputOffset(mapper.breakOffsets, i); if (previousLineIndex !== r.outputLineIndex) { previousLineIndex = r.outputLineIndex; actualAnnotatedText += '|'; @@ -114,7 +114,7 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { test('issue #35162: wrappingIndent not consistently working', () => { let factory = new CharacterHardWrappingLineMapperFactory('', '\t '); let mapper = assertLineMapping(factory, 4, 24, ' t h i s |i s |a l |o n |g l |i n |e', WrappingIndent.Indent); - assert.equal(mapper!.getWrappedLinesIndent(), ' \t'); + assert.equal(mapper!.wrappedLinesIndent, ' \t'); }); test('issue #75494: surrogate pairs', () => { @@ -125,6 +125,6 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { test('CharacterHardWrappingLineMapper - WrappingIndent.DeepIndent', () => { let factory = new CharacterHardWrappingLineMapperFactory('', '\t '); let mapper = assertLineMapping(factory, 4, 26, ' W e A r e T e s t |i n g D e |e p I n d |e n t a t |i o n', WrappingIndent.DeepIndent); - assert.equal(mapper!.getWrappedLinesIndent(), ' \t\t'); + assert.equal(mapper!.wrappedLinesIndent, ' \t\t'); }); }); diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index 833d0d7313b..52dc680d78a 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -13,8 +13,8 @@ import { EndOfLinePreference } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import * as modes from 'vs/editor/common/modes'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; -import { CharacterHardWrappingLineMapperFactory, CharacterHardWrappingLineMapping } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; -import { ILineMapping, ISimpleModel, SplitLine, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; +import { LineBreakingData, ISimpleModel, SplitLine, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -776,12 +776,12 @@ function createSplitLine(splitLengths: number[], wrappedLinesPrefix: string, isV return new SplitLine(createLineMapping(splitLengths, wrappedLinesPrefix), isVisible); } -function createLineMapping(breakingLengths: number[], wrappedLinesPrefix: string): ILineMapping { +function createLineMapping(breakingLengths: number[], wrappedLinesPrefix: string): LineBreakingData { let sums: number[] = []; for (let i = 0; i < breakingLengths.length; i++) { sums[i] = (i > 0 ? sums[i - 1] : 0) + breakingLengths[i]; } - return new CharacterHardWrappingLineMapping(sums, wrappedLinesPrefix); + return new LineBreakingData(sums, wrappedLinesPrefix); } function createModel(text: string): ISimpleModel { From 53ebe7809c8cb08a67e803d7669ea621b15755ee Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 7 Jan 2020 17:03:18 +0100 Subject: [PATCH 054/843] rename input field polish --- .../editor/contrib/rename/renameInputField.ts | 52 +++++++------------ 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/src/vs/editor/contrib/rename/renameInputField.ts b/src/vs/editor/contrib/rename/renameInputField.ts index 5f36cd8ae21..815cc4a34ad 100644 --- a/src/vs/editor/contrib/rename/renameInputField.ts +++ b/src/vs/editor/contrib/rename/renameInputField.ts @@ -19,7 +19,7 @@ export const CONTEXT_RENAME_INPUT_VISIBLE = new RawContextKey('renameIn export class RenameInputField implements IContentWidget, IDisposable { - private _editor: ICodeEditor; + private _position?: Position; private _domNode?: HTMLElement; private _inputField?: HTMLInputElement; @@ -31,38 +31,33 @@ export class RenameInputField implements IContentWidget, IDisposable { allowEditorOverflow: boolean = true; constructor( - editor: ICodeEditor, - private readonly themeService: IThemeService, + private readonly _editor: ICodeEditor, + private readonly _themeService: IThemeService, contextKeyService: IContextKeyService, ) { this._visibleContextKey = CONTEXT_RENAME_INPUT_VISIBLE.bindTo(contextKeyService); - this._editor = editor; this._editor.addContentWidget(this); - this._disposables.add(editor.onDidChangeConfiguration(e => { + this._disposables.add(this._editor.onDidChangeConfiguration(e => { if (e.hasChanged(EditorOption.fontInfo)) { - this.updateFont(); + this._updateFont(); } })); - this._disposables.add(themeService.onThemeChange(theme => this.onThemeChange(theme))); + this._disposables.add(_themeService.onThemeChange(this._updateStyles, this)); } - private onThemeChange(theme: ITheme): void { - this.updateStyles(theme); - } - - public dispose(): void { + dispose(): void { this._disposables.dispose(); this._editor.removeContentWidget(this); } - public getId(): string { + getId(): string { return '__renameInputWidget'; } - public getDomNode(): HTMLElement { + getDomNode(): HTMLElement { if (!this._domNode) { this._inputField = document.createElement('input'); this._inputField.className = 'rename-input'; @@ -73,13 +68,13 @@ export class RenameInputField implements IContentWidget, IDisposable { this._domNode.className = 'monaco-editor rename-box'; this._domNode.appendChild(this._inputField); - this.updateFont(); - this.updateStyles(this.themeService.getTheme()); + this._updateFont(); + this._updateStyles(this._themeService.getTheme()); } return this._domNode; } - private updateStyles(theme: ITheme): void { + private _updateStyles(theme: ITheme): void { if (!this._inputField) { return; } @@ -99,7 +94,7 @@ export class RenameInputField implements IContentWidget, IDisposable { this._domNode!.style.boxShadow = widgetShadowColor ? ` 0 2px 8px ${widgetShadowColor}` : ''; } - private updateFont(): void { + private _updateFont(): void { if (!this._inputField) { return; } @@ -110,7 +105,7 @@ export class RenameInputField implements IContentWidget, IDisposable { this._inputField.style.fontSize = `${fontInfo.fontSize}px`; } - public getPosition(): IContentWidgetPosition | null { + getPosition(): IContentWidgetPosition | null { return this._visible ? { position: this._position!, preference: [ContentWidgetPositionPreference.BELOW, ContentWidgetPositionPreference.ABOVE] } : null; @@ -119,19 +114,19 @@ export class RenameInputField implements IContentWidget, IDisposable { private _currentAcceptInput: (() => void) | null = null; private _currentCancelInput: ((focusEditor: boolean) => void) | null = null; - public acceptInput(): void { + acceptInput(): void { if (this._currentAcceptInput) { this._currentAcceptInput(); } } - public cancelInput(focusEditor: boolean): void { + cancelInput(focusEditor: boolean): void { if (this._currentCancelInput) { this._currentCancelInput(focusEditor); } } - public getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number): Promise { + getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number): Promise { this._position = new Position(where.startLineNumber, where.startColumn); this._inputField!.value = value; @@ -140,10 +135,6 @@ export class RenameInputField implements IContentWidget, IDisposable { this._inputField!.size = Math.max((where.endColumn - where.startColumn) * 1.1, 20); const disposeOnDone = new DisposableStore(); - const always = () => { - disposeOnDone.dispose(); - this._hide(); - }; return new Promise(resolve => { @@ -178,12 +169,9 @@ export class RenameInputField implements IContentWidget, IDisposable { this._show(); - }).then(newValue => { - always(); - return newValue; - }, err => { - always(); - return Promise.reject(err); + }).finally(() => { + disposeOnDone.dispose(); + this._hide(); }); } From b0bd81383921f8f0234021a7d02466b3f01b57f9 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 7 Jan 2020 17:32:18 +0100 Subject: [PATCH 055/843] Switch to use a LineNumberMapper for view line - model line mapping --- .../common/viewModel/prefixSumComputer.ts | 70 -------- .../common/viewModel/splitLinesCollection.ts | 157 ++++++++++++------ .../editor/common/viewModel/viewModelImpl.ts | 2 - 3 files changed, 106 insertions(+), 123 deletions(-) diff --git a/src/vs/editor/common/viewModel/prefixSumComputer.ts b/src/vs/editor/common/viewModel/prefixSumComputer.ts index d4e8c1f0df2..6cb1312c07e 100644 --- a/src/vs/editor/common/viewModel/prefixSumComputer.ts +++ b/src/vs/editor/common/viewModel/prefixSumComputer.ts @@ -187,73 +187,3 @@ export class PrefixSumComputer { return new PrefixSumIndexOfResult(mid, accumulatedValue - midStart); } } - -export class PrefixSumComputerWithCache { - - private readonly _actual: PrefixSumComputer; - private _cacheAccumulatedValueStart: number = 0; - private _cache: PrefixSumIndexOfResult[] | null = null; - - constructor(values: Uint32Array) { - this._actual = new PrefixSumComputer(values); - this._bustCache(); - } - - private _bustCache(): void { - this._cacheAccumulatedValueStart = 0; - this._cache = null; - } - - public insertValues(insertIndex: number, insertValues: Uint32Array): void { - if (this._actual.insertValues(insertIndex, insertValues)) { - this._bustCache(); - } - } - - public changeValue(index: number, value: number): void { - if (this._actual.changeValue(index, value)) { - this._bustCache(); - } - } - - public removeValues(startIndex: number, cnt: number): void { - if (this._actual.removeValues(startIndex, cnt)) { - this._bustCache(); - } - } - - public getTotalValue(): number { - return this._actual.getTotalValue(); - } - - public getAccumulatedValue(index: number): number { - return this._actual.getAccumulatedValue(index); - } - - public getIndexOf(accumulatedValue: number): PrefixSumIndexOfResult { - accumulatedValue = Math.floor(accumulatedValue); //@perf - - if (this._cache !== null) { - let cacheIndex = accumulatedValue - this._cacheAccumulatedValueStart; - if (cacheIndex >= 0 && cacheIndex < this._cache.length) { - // Cache hit! - return this._cache[cacheIndex]; - } - } - - // Cache miss! - return this._actual.getIndexOf(accumulatedValue); - } - - /** - * Gives a hint that a lot of requests are about to come in for these accumulated values. - */ - public warmUpCache(accumulatedValueStart: number, accumulatedValueEnd: number): void { - let newCache: PrefixSumIndexOfResult[] = []; - for (let accumulatedValue = accumulatedValueStart; accumulatedValue <= accumulatedValueEnd; accumulatedValue++) { - newCache[accumulatedValue - accumulatedValueStart] = this.getIndexOf(accumulatedValue); - } - this._cache = newCache; - this._cacheAccumulatedValueStart = accumulatedValueStart; - } -} diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 1b64f46d59e..f90d1dd6372 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as arrays from 'vs/base/common/arrays'; import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { Position } from 'vs/editor/common/core/position'; @@ -10,7 +11,7 @@ import { IRange, Range } from 'vs/editor/common/core/range'; import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecoration, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { PrefixSumComputerWithCache } from 'vs/editor/common/viewModel/prefixSumComputer'; +import { PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer'; import { ICoordinatesConverter, IOverviewRulerDecorations, ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { ITheme } from 'vs/platform/theme/common/themeService'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -43,13 +44,12 @@ export class LineBreakingData { let low = 0; let high = breakOffsets.length - 1; let mid = 0; - let midStop = 0; let midStart = 0; while (low <= high) { mid = low + ((high - low) / 2) | 0; - midStop = breakOffsets[mid]; + const midStop = breakOffsets[mid]; midStart = mid > 0 ? breakOffsets[mid - 1] : 0; if (inputOffset < midStart) { @@ -116,7 +116,6 @@ export interface IViewModelLinesCollection extends IDisposable { acceptVersionId(versionId: number): void; getViewLineCount(): number; - warmUpLookupCache(viewStartLineNumber: number, viewEndLineNumber: number): void; getActiveIndentGuide(viewLineNumber: number, minLineNumber: number, maxLineNumber: number): IActiveIndentGuideInfo; getViewLinesIndentGuides(viewStartLineNumber: number, viewEndLineNumber: number): number[]; getViewLineContent(viewLineNumber: number): string; @@ -145,9 +144,7 @@ export class CoordinatesConverter implements ICoordinatesConverter { } public convertViewRangeToModelRange(viewRange: Range): Range { - let start = this._lines.convertViewPositionToModelPosition(viewRange.startLineNumber, viewRange.startColumn); - let end = this._lines.convertViewPositionToModelPosition(viewRange.endLineNumber, viewRange.endColumn); - return new Range(start.lineNumber, start.column, end.lineNumber, end.column); + return this._lines.convertViewRangeToModelRange(viewRange); } public validateViewPosition(viewPosition: Position, expectedModelPosition: Position): Position { @@ -155,9 +152,7 @@ export class CoordinatesConverter implements ICoordinatesConverter { } public validateViewRange(viewRange: Range, expectedModelRange: Range): Range { - const validViewStart = this._lines.validateViewPosition(viewRange.startLineNumber, viewRange.startColumn, expectedModelRange.getStartPosition()); - const validViewEnd = this._lines.validateViewPosition(viewRange.endLineNumber, viewRange.endColumn, expectedModelRange.getEndPosition()); - return new Range(validViewStart.lineNumber, validViewStart.column, validViewEnd.lineNumber, validViewEnd.column); + return this._lines.validateViewRange(viewRange, expectedModelRange); } // Model -> View conversion and related methods @@ -173,7 +168,6 @@ export class CoordinatesConverter implements ICoordinatesConverter { public modelPositionIsVisible(modelPosition: Position): boolean { return this._lines.modelPositionIsVisible(modelPosition.lineNumber, modelPosition.column); } - } const enum IndentGuideRepeatOption { @@ -182,6 +176,89 @@ const enum IndentGuideRepeatOption { BlockAll = 2 } +class LineNumberMapper { + + private _counts: number[]; + private _isValid: boolean; + private _validEndIndex: number; + + private _modelToView: number[]; + private _viewToModel: number[]; + + constructor(viewLineCounts: number[]) { + this._counts = viewLineCounts; + this._isValid = false; + this._validEndIndex = -1; + this._modelToView = []; + this._viewToModel = []; + } + + private _invalidate(index: number): void { + this._isValid = false; + this._validEndIndex = Math.min(this._validEndIndex, index - 1); + } + + private _ensureValid(): void { + if (this._isValid) { + return; + } + + for (let i = this._validEndIndex + 1, len = this._counts.length; i < len; i++) { + const viewLineCount = this._counts[i]; + const viewLinesAbove = (i > 0 ? this._modelToView[i - 1] : 0); + + this._modelToView[i] = viewLinesAbove + viewLineCount; + for (let j = 0; j < viewLineCount; j++) { + this._viewToModel[viewLinesAbove + j] = i; + } + } + + // trim things + this._modelToView.length = this._counts.length; + this._viewToModel.length = this._modelToView[this._modelToView.length - 1]; + + // mark as valid + this._isValid = true; + this._validEndIndex = this._counts.length - 1; + } + + public changeValue(index: number, value: number): void { + if (this._counts[index] === value) { + // no change + return; + } + this._counts[index] = value; + this._invalidate(index); + } + + public removeValues(start: number, deleteCount: number): void { + this._counts.splice(start, deleteCount); + this._invalidate(start); + } + + public insertValues(insertIndex: number, insertArr: number[]): void { + this._counts = arrays.arrayInsert(this._counts, insertIndex, insertArr); + this._invalidate(insertIndex); + } + + public getTotalValue(): number { + this._ensureValid(); + return this._viewToModel.length; + } + + public getAccumulatedValue(index: number): number { + this._ensureValid(); + return this._modelToView[index]; + } + + public getIndexOf(accumulatedValue: number): PrefixSumIndexOfResult { + this._ensureValid(); + const modelLineIndex = this._viewToModel[accumulatedValue]; + const viewLinesAbove = (modelLineIndex > 0 ? this._modelToView[modelLineIndex - 1] : 0); + return new PrefixSumIndexOfResult(modelLineIndex, accumulatedValue - viewLinesAbove); + } +} + export class SplitLinesCollection implements IViewModelLinesCollection { private readonly model: ITextModel; @@ -193,7 +270,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { private tabSize: number; private lines!: ISplitLine[]; - private prefixSumComputer!: PrefixSumComputerWithCache; + private prefixSumComputer!: LineNumberMapper; private readonly linePositionMapperFactory: ILineMapperFactory; @@ -219,18 +296,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return new CoordinatesConverter(this); } - private _ensureValidState(): void { - let modelVersion = this.model.getVersionId(); - if (modelVersion !== this._validModelVersionId) { - // This is pretty bad, it means we lost track of the model... - throw new Error(`ViewModel is out of sync with Model!`); - } - if (this.lines.length !== this.model.getLineCount()) { - // This is pretty bad, it means we lost track of the model... - this._constructLines(false); - } - } - private _constructLines(resetHiddenAreas: boolean): void { this.lines = []; @@ -246,7 +311,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } const lineMappings = lineMappingComputer.finalize(); - let values = new Uint32Array(lineCount); + let values: number[] = []; let hiddenAreas = this.hiddenAreasIds.map((areaId) => this.model.getDecorationRange(areaId)!).sort(Range.compareRangesUsingStarts); let hiddenAreaStart = 1, hiddenAreaEnd = 0; @@ -271,7 +336,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { this._validModelVersionId = this.model.getVersionId(); - this.prefixSumComputer = new PrefixSumComputerWithCache(values); + this.prefixSumComputer = new LineNumberMapper(values); } public getHiddenAreas(): Range[] { @@ -459,7 +524,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { let totalOutputLineCount = 0; let insertLines: ISplitLine[] = []; - let insertPrefixSumValues = new Uint32Array(linesMappings.length); + let insertPrefixSumValues: number[] = []; for (let i = 0, len = linesMappings.length; i < len; i++) { let line = createSplitLine(linesMappings[i], !isInHiddenArea); @@ -536,7 +601,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLineCount(): number { - this._ensureValidState(); return this.prefixSumComputer.getTotalValue(); } @@ -544,22 +608,14 @@ export class SplitLinesCollection implements IViewModelLinesCollection { if (viewLineNumber < 1) { return 1; } - let viewLineCount = this.getViewLineCount(); + const viewLineCount = this.getViewLineCount(); if (viewLineNumber > viewLineCount) { return viewLineCount; } - return viewLineNumber; - } - - /** - * Gives a hint that a lot of requests are about to come in for these line numbers. - */ - public warmUpLookupCache(viewStartLineNumber: number, viewEndLineNumber: number): void { - this.prefixSumComputer.warmUpCache(viewStartLineNumber - 1, viewEndLineNumber - 1); + return viewLineNumber | 0; } public getActiveIndentGuide(viewLineNumber: number, minLineNumber: number, maxLineNumber: number): IActiveIndentGuideInfo { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); minLineNumber = this._toValidViewLineNumber(minLineNumber); maxLineNumber = this._toValidViewLineNumber(maxLineNumber); @@ -579,7 +635,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLinesIndentGuides(viewStartLineNumber: number, viewEndLineNumber: number): number[] { - this._ensureValidState(); viewStartLineNumber = this._toValidViewLineNumber(viewStartLineNumber); viewEndLineNumber = this._toValidViewLineNumber(viewEndLineNumber); @@ -650,7 +705,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLineContent(viewLineNumber: number): string { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); let lineIndex = r.index; @@ -660,7 +714,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLineLength(viewLineNumber: number): number { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); let lineIndex = r.index; @@ -670,7 +723,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLineMinColumn(viewLineNumber: number): number { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); let lineIndex = r.index; @@ -680,7 +732,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLineMaxColumn(viewLineNumber: number): number { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); let lineIndex = r.index; @@ -690,7 +741,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLineData(viewLineNumber: number): ViewLineData { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); let lineIndex = r.index; @@ -700,7 +750,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): ViewLineData[] { - this._ensureValidState(); viewStartLineNumber = this._toValidViewLineNumber(viewStartLineNumber); viewEndLineNumber = this._toValidViewLineNumber(viewEndLineNumber); @@ -739,7 +788,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public validateViewPosition(viewLineNumber: number, viewColumn: number, expectedModelPosition: Position): Position { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); @@ -767,8 +815,13 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return this.convertModelPositionToViewPosition(expectedModelPosition.lineNumber, expectedModelPosition.column); } + public validateViewRange(viewRange: Range, expectedModelRange: Range): Range { + const validViewStart = this.validateViewPosition(viewRange.startLineNumber, viewRange.startColumn, expectedModelRange.getStartPosition()); + const validViewEnd = this.validateViewPosition(viewRange.endLineNumber, viewRange.endColumn, expectedModelRange.getEndPosition()); + return new Range(validViewStart.lineNumber, validViewStart.column, validViewEnd.lineNumber, validViewEnd.column); + } + public convertViewPositionToModelPosition(viewLineNumber: number, viewColumn: number): Position { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); @@ -780,8 +833,13 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return this.model.validatePosition(new Position(lineIndex + 1, inputColumn)); } + public convertViewRangeToModelRange(viewRange: Range): Range { + const start = this.convertViewPositionToModelPosition(viewRange.startLineNumber, viewRange.startColumn); + const end = this.convertViewPositionToModelPosition(viewRange.endLineNumber, viewRange.endColumn); + return new Range(start.lineNumber, start.column, end.lineNumber, end.column); + } + public convertModelPositionToViewPosition(_modelLineNumber: number, _modelColumn: number): Position { - this._ensureValidState(); const validPosition = this.model.validatePosition(new Position(_modelLineNumber, _modelColumn)); const inputLineNumber = validPosition.lineNumber; @@ -1374,9 +1432,6 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return this.model.getLineCount(); } - public warmUpLookupCache(_viewStartLineNumber: number, _viewEndLineNumber: number): void { - } - public getActiveIndentGuide(viewLineNumber: number, _minLineNumber: number, _maxLineNumber: number): IActiveIndentGuideInfo { return { startLineNumber: viewLineNumber, diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 3bb303641c7..229df6e3614 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -495,8 +495,6 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel * Gives a hint that a lot of requests are about to come in for these line numbers. */ public setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void { - this.lines.warmUpLookupCache(startLineNumber, endLineNumber); - this.viewportStartLine = startLineNumber; let position = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(startLineNumber, this.getLineMinColumn(startLineNumber))); this.viewportStartLineTrackedRange = this.model._setTrackedRange(this.viewportStartLineTrackedRange, new Range(position.lineNumber, position.column, position.lineNumber, position.column), TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges); From 405707fec77242a1b2e335f3f8f27716dc3bb3a3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 7 Jan 2020 17:44:38 +0100 Subject: [PATCH 056/843] add Shift+Enter for acceptRenameWithPreview --- src/vs/editor/contrib/rename/rename.ts | 34 +++++++++++++------ .../editor/contrib/rename/renameInputField.ts | 30 +++++++++------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index f85e8d9d80e..39ffb621870 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -176,11 +176,11 @@ class RenameController implements IEditorContribution { selectionEnd = Math.min(loc.range.endColumn, selection.endColumn) - loc.range.startColumn; } - const newNameOrFocusFlag = await this._renameInputField.getValue().getInput(loc.range, loc.text, selectionStart, selectionEnd); + const inputFieldResult = await this._renameInputField.getValue().getInput(loc.range, loc.text, selectionStart, selectionEnd); - - if (typeof newNameOrFocusFlag === 'boolean') { - if (newNameOrFocusFlag) { + // no result, only hint to focus the editor or not + if (typeof inputFieldResult === 'boolean') { + if (inputFieldResult) { this.editor.focus(); } return undefined; @@ -188,7 +188,7 @@ class RenameController implements IEditorContribution { this.editor.focus(); - const renameOperation = raceCancellation(skeleton.provideRenameEdits(newNameOrFocusFlag, 0, [], this._cts.token), this._cts.token).then(async renameResult => { + const renameOperation = raceCancellation(skeleton.provideRenameEdits(inputFieldResult.newName, 0, [], this._cts.token), this._cts.token).then(async renameResult => { if (!renameResult || !this.editor.hasModel()) { return; @@ -199,9 +199,12 @@ class RenameController implements IEditorContribution { return; } - this._bulkEditService.apply(renameResult, { editor: this.editor }).then(result => { + this._bulkEditService.apply(renameResult, { + editor: this.editor, + noPreview: !inputFieldResult.wantsPreview + }).then(result => { if (result.ariaSummary) { - alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, newNameOrFocusFlag, result.ariaSummary)); + alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, inputFieldResult.newName, result.ariaSummary)); } }).catch(err => { this._notificationService.error(nls.localize('rename.failedApply', "Rename failed to apply edits")); @@ -218,8 +221,8 @@ class RenameController implements IEditorContribution { } - acceptRenameInput(): void { - this._renameInputField.getValue().acceptInput(); + acceptRenameInput(wantsPreview: boolean): void { + this._renameInputField.getValue().acceptInput(wantsPreview); } cancelRenameInput(): void { @@ -286,7 +289,7 @@ const RenameCommand = EditorCommand.bindToContribution(RenameC registerEditorCommand(new RenameCommand({ id: 'acceptRenameInput', precondition: CONTEXT_RENAME_INPUT_VISIBLE, - handler: x => x.acceptRenameInput(), + handler: x => x.acceptRenameInput(false), kbOpts: { weight: KeybindingWeight.EditorContrib + 99, kbExpr: EditorContextKeys.focus, @@ -294,6 +297,17 @@ registerEditorCommand(new RenameCommand({ } })); +registerEditorCommand(new RenameCommand({ + id: 'acceptRenameInputWithPreview', + precondition: CONTEXT_RENAME_INPUT_VISIBLE, + handler: x => x.acceptRenameInput(true), + kbOpts: { + weight: KeybindingWeight.EditorContrib + 99, + kbExpr: EditorContextKeys.focus, + primary: KeyMod.Shift + KeyCode.Enter + } +})); + registerEditorCommand(new RenameCommand({ id: 'cancelRenameInput', precondition: CONTEXT_RENAME_INPUT_VISIBLE, diff --git a/src/vs/editor/contrib/rename/renameInputField.ts b/src/vs/editor/contrib/rename/renameInputField.ts index 815cc4a34ad..ec9c5f10cc7 100644 --- a/src/vs/editor/contrib/rename/renameInputField.ts +++ b/src/vs/editor/contrib/rename/renameInputField.ts @@ -17,8 +17,12 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; export const CONTEXT_RENAME_INPUT_VISIBLE = new RawContextKey('renameInputVisible', false); -export class RenameInputField implements IContentWidget, IDisposable { +export interface RenameInputFieldResult { + newName: string; + wantsPreview?: boolean; +} +export class RenameInputField implements IContentWidget, IDisposable { private _position?: Position; private _domNode?: HTMLElement; @@ -111,12 +115,12 @@ export class RenameInputField implements IContentWidget, IDisposable { : null; } - private _currentAcceptInput: (() => void) | null = null; - private _currentCancelInput: ((focusEditor: boolean) => void) | null = null; + private _currentAcceptInput?: (wantsPreview: boolean) => void; + private _currentCancelInput?: (focusEditor: boolean) => void; - acceptInput(): void { + acceptInput(wantsPreview: boolean): void { if (this._currentAcceptInput) { - this._currentAcceptInput(); + this._currentAcceptInput(wantsPreview); } } @@ -126,7 +130,7 @@ export class RenameInputField implements IContentWidget, IDisposable { } } - getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number): Promise { + getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number): Promise { this._position = new Position(where.startLineNumber, where.startColumn); this._inputField!.value = value; @@ -136,25 +140,25 @@ export class RenameInputField implements IContentWidget, IDisposable { const disposeOnDone = new DisposableStore(); - return new Promise(resolve => { + return new Promise(resolve => { this._currentCancelInput = (focusEditor) => { - this._currentAcceptInput = null; - this._currentCancelInput = null; + this._currentAcceptInput = undefined; + this._currentCancelInput = undefined; resolve(focusEditor); return true; }; - this._currentAcceptInput = () => { + this._currentAcceptInput = (wantsPreview) => { if (this._inputField!.value.trim().length === 0 || this._inputField!.value === value) { // empty or whitespace only or not changed this.cancelInput(true); return; } - this._currentAcceptInput = null; - this._currentCancelInput = null; - resolve(this._inputField!.value); + this._currentAcceptInput = undefined; + this._currentCancelInput = undefined; + resolve({ newName: this._inputField!.value, wantsPreview }); }; let onCursorChanged = () => { From beb6b203a665627bed10ee0285b5bfdf9dc48fbf Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 7 Jan 2020 18:24:39 +0100 Subject: [PATCH 057/843] show rename, preview labels in rename input box --- src/vs/editor/contrib/rename/rename.ts | 9 +- .../contrib/rename/renameInputField.css | 2 + .../editor/contrib/rename/renameInputField.ts | 107 +++++++++++------- 3 files changed, 70 insertions(+), 48 deletions(-) diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 39ffb621870..29b4bb8f7cf 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { illegalArgument, onUnexpectedError } from 'vs/base/common/errors'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand, registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; @@ -14,7 +14,6 @@ import { ITextModel } from 'vs/editor/common/model'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { RenameInputField, CONTEXT_RENAME_INPUT_VISIBLE } from './renameInputField'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; import { WorkspaceEdit, RenameProviderRegistry, RenameProvider, RenameLocation, Rejection } from 'vs/editor/common/modes'; import { Position, IPosition } from 'vs/editor/common/core/position'; import { alert } from 'vs/base/browser/ui/aria/aria'; @@ -31,6 +30,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { IdleValue, raceCancellation } from 'vs/base/common/async'; import { withNullAsUndefined } from 'vs/base/common/types'; import { ILogService } from 'vs/platform/log/common/log'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; class RenameSkeleton { @@ -110,14 +110,13 @@ class RenameController implements IEditorContribution { constructor( private readonly editor: ICodeEditor, + @IInstantiationService private readonly _instaService: IInstantiationService, @INotificationService private readonly _notificationService: INotificationService, @IBulkEditService private readonly _bulkEditService: IBulkEditService, @IEditorProgressService private readonly _progressService: IEditorProgressService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IThemeService private readonly _themeService: IThemeService, @ILogService private readonly _logService: ILogService, ) { - this._renameInputField = this._dispoableStore.add(new IdleValue(() => this._dispoableStore.add(new RenameInputField(this.editor, this._themeService, this._contextKeyService)))); + this._renameInputField = this._dispoableStore.add(new IdleValue(() => this._dispoableStore.add(this._instaService.createInstance(RenameInputField, this.editor, ['acceptRenameInput', 'acceptRenameInputWithPreview'])))); } dispose(): void { diff --git a/src/vs/editor/contrib/rename/renameInputField.css b/src/vs/editor/contrib/rename/renameInputField.css index 0478cd7ce48..a61b3741c2f 100644 --- a/src/vs/editor/contrib/rename/renameInputField.css +++ b/src/vs/editor/contrib/rename/renameInputField.css @@ -6,8 +6,10 @@ .monaco-editor .rename-box { z-index: 100; color: inherit; + padding: 4px; } .monaco-editor .rename-box .rename-input { padding: 4px; + width: calc(100% - 8px); } diff --git a/src/vs/editor/contrib/rename/renameInputField.ts b/src/vs/editor/contrib/rename/renameInputField.ts index ec9c5f10cc7..f70ed3c15d1 100644 --- a/src/vs/editor/contrib/rename/renameInputField.ts +++ b/src/vs/editor/contrib/rename/renameInputField.ts @@ -4,16 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./renameInputField'; -import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { inputBackground, inputBorder, inputForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { inputBackground, inputBorder, inputForeground, widgetShadow, editorWidgetBackground } from 'vs/platform/theme/common/colorRegistry'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; export const CONTEXT_RENAME_INPUT_VISIBLE = new RawContextKey('renameInputVisible', false); @@ -22,22 +23,24 @@ export interface RenameInputFieldResult { wantsPreview?: boolean; } -export class RenameInputField implements IContentWidget, IDisposable { +export class RenameInputField implements IContentWidget { private _position?: Position; private _domNode?: HTMLElement; - private _inputField?: HTMLInputElement; + private _input?: HTMLInputElement; + private _label?: HTMLDivElement; private _visible?: boolean; private readonly _visibleContextKey: IContextKey; private readonly _disposables = new DisposableStore(); - // Editor.IContentWidget.allowEditorOverflow - allowEditorOverflow: boolean = true; + readonly allowEditorOverflow: boolean = true; constructor( private readonly _editor: ICodeEditor, - private readonly _themeService: IThemeService, - contextKeyService: IContextKeyService, + private readonly _acceptKeybindings: [string, string], + @IThemeService private readonly _themeService: IThemeService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, + @IContextKeyService contextKeyService: IContextKeyService, ) { this._visibleContextKey = CONTEXT_RENAME_INPUT_VISIBLE.bindTo(contextKeyService); @@ -63,14 +66,25 @@ export class RenameInputField implements IContentWidget, IDisposable { getDomNode(): HTMLElement { if (!this._domNode) { - this._inputField = document.createElement('input'); - this._inputField.className = 'rename-input'; - this._inputField.type = 'text'; - this._inputField.setAttribute('aria-label', localize('renameAriaLabel', "Rename input. Type new name and press Enter to commit.")); this._domNode = document.createElement('div'); - this._domNode.style.height = `${this._editor.getOption(EditorOption.lineHeight)}px`; this._domNode.className = 'monaco-editor rename-box'; - this._domNode.appendChild(this._inputField); + + this._input = document.createElement('input'); + this._input.className = 'rename-input'; + this._input.type = 'text'; + this._input.setAttribute('aria-label', localize('renameAriaLabel', "Rename input. Type new name and press Enter to commit.")); + this._domNode.appendChild(this._input); + + this._label = document.createElement('div'); + this._label.className = 'rename-label'; + this._domNode.appendChild(this._label); + const updateLabel = () => { + const [accept, preview] = this._acceptKeybindings; + this._keybindingService.lookupKeybinding(accept); + this._label!.innerText = localize('label', "Press {0} to Rename, {1} to Preview", this._keybindingService.lookupKeybinding(accept)?.getLabel(), this._keybindingService.lookupKeybinding(preview)?.getLabel()); + }; + updateLabel(); + this._disposables.add(this._keybindingService.onDidUpdateKeybindings(updateLabel)); this._updateFont(); this._updateStyles(this._themeService.getTheme()); @@ -79,40 +93,44 @@ export class RenameInputField implements IContentWidget, IDisposable { } private _updateStyles(theme: ITheme): void { - if (!this._inputField) { + if (!this._input || !this._domNode) { return; } - const background = theme.getColor(inputBackground); - const foreground = theme.getColor(inputForeground); const widgetShadowColor = theme.getColor(widgetShadow); + this._domNode.style.backgroundColor = String(theme.getColor(editorWidgetBackground) ?? ''); + this._domNode.style.boxShadow = widgetShadowColor ? ` 0 2px 8px ${widgetShadowColor}` : ''; + this._domNode.style.color = String(theme.getColor(inputForeground) ?? ''); + + this._input.style.backgroundColor = String(theme.getColor(inputBackground) ?? ''); + // this._input.style.color = String(theme.getColor(inputForeground) ?? ''); const border = theme.getColor(inputBorder); - - this._inputField.style.backgroundColor = background ? background.toString() : ''; - this._inputField.style.color = foreground ? foreground.toString() : ''; - - this._inputField.style.borderWidth = border ? '1px' : '0px'; - this._inputField.style.borderStyle = border ? 'solid' : 'none'; - this._inputField.style.borderColor = border ? border.toString() : 'none'; - - this._domNode!.style.boxShadow = widgetShadowColor ? ` 0 2px 8px ${widgetShadowColor}` : ''; + this._input.style.borderWidth = border ? '1px' : '0px'; + this._input.style.borderStyle = border ? 'solid' : 'none'; + this._input.style.borderColor = border?.toString() ?? 'none'; } private _updateFont(): void { - if (!this._inputField) { + if (!this._input || !this._label) { return; } const fontInfo = this._editor.getOption(EditorOption.fontInfo); - this._inputField.style.fontFamily = fontInfo.fontFamily; - this._inputField.style.fontWeight = fontInfo.fontWeight; - this._inputField.style.fontSize = `${fontInfo.fontSize}px`; + this._input.style.fontFamily = fontInfo.fontFamily; + this._input.style.fontWeight = fontInfo.fontWeight; + this._input.style.fontSize = `${fontInfo.fontSize}px`; + + this._label.style.fontSize = `${fontInfo.fontSize * 0.8}px`; } getPosition(): IContentWidgetPosition | null { - return this._visible - ? { position: this._position!, preference: [ContentWidgetPositionPreference.BELOW, ContentWidgetPositionPreference.ABOVE] } - : null; + if (!this._visible) { + return null; + } + return { + position: this._position!, + preference: [ContentWidgetPositionPreference.BELOW, ContentWidgetPositionPreference.ABOVE] + }; } private _currentAcceptInput?: (wantsPreview: boolean) => void; @@ -133,10 +151,10 @@ export class RenameInputField implements IContentWidget, IDisposable { getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number): Promise { this._position = new Position(where.startLineNumber, where.startColumn); - this._inputField!.value = value; - this._inputField!.setAttribute('selectionStart', selectionStart.toString()); - this._inputField!.setAttribute('selectionEnd', selectionEnd.toString()); - this._inputField!.size = Math.max((where.endColumn - where.startColumn) * 1.1, 20); + this._input!.value = value; + this._input!.setAttribute('selectionStart', selectionStart.toString()); + this._input!.setAttribute('selectionEnd', selectionEnd.toString()); + this._input!.size = Math.max((where.endColumn - where.startColumn) * 1.1, 20); const disposeOnDone = new DisposableStore(); @@ -150,7 +168,7 @@ export class RenameInputField implements IContentWidget, IDisposable { }; this._currentAcceptInput = (wantsPreview) => { - if (this._inputField!.value.trim().length === 0 || this._inputField!.value === value) { + if (this._input!.value.trim().length === 0 || this._input!.value === value) { // empty or whitespace only or not changed this.cancelInput(true); return; @@ -158,7 +176,10 @@ export class RenameInputField implements IContentWidget, IDisposable { this._currentAcceptInput = undefined; this._currentCancelInput = undefined; - resolve({ newName: this._inputField!.value, wantsPreview }); + resolve({ + newName: this._input!.value, + wantsPreview + }); }; let onCursorChanged = () => { @@ -186,10 +207,10 @@ export class RenameInputField implements IContentWidget, IDisposable { this._editor.layoutContentWidget(this); setTimeout(() => { - this._inputField!.focus(); - this._inputField!.setSelectionRange( - parseInt(this._inputField!.getAttribute('selectionStart')!), - parseInt(this._inputField!.getAttribute('selectionEnd')!)); + this._input!.focus(); + this._input!.setSelectionRange( + parseInt(this._input!.getAttribute('selectionStart')!), + parseInt(this._input!.getAttribute('selectionEnd')!)); }, 100); } From 406ac6a34f096a6ce71626d36d0c38e08a025464 Mon Sep 17 00:00:00 2001 From: Konstantin Solomatov Date: Tue, 7 Jan 2020 12:26:06 -0800 Subject: [PATCH 058/843] Code cleanup --- .../contrib/terminal/browser/terminalPanel.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 0cec6b4b0c6..cfa67e91d8f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -97,19 +97,14 @@ export class TerminalPanel extends Panel { this._register(this.onDidChangeVisibility(visible => { if (visible) { - if (this._terminalService.terminalInstances.length > 0) { - this._updateFont(); - this._updateTheme(); + const hasTerminals = this._terminalService.terminalInstances.length > 0; + if (!hasTerminals) { + this._terminalService.createTerminal(); + } + this._updateFont(); + this._updateTheme(); + if (!hasTerminals) { this._terminalService.getActiveTab()?.setVisible(visible); - } else { - // Check if instances were already restored as part of workbench restore - if (this._terminalService.terminalInstances.length === 0) { - this._terminalService.createTerminal(); - } - if (this._terminalService.terminalInstances.length > 0) { - this._updateFont(); - this._updateTheme(); - } } } })); From 22b031930baf9e8f5b610c655c5057b16571a24b Mon Sep 17 00:00:00 2001 From: Konstantin Solomatov Date: Tue, 7 Jan 2020 13:03:11 -0800 Subject: [PATCH 059/843] Fix incorrect negation --- src/vs/workbench/contrib/terminal/browser/terminalPanel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index cfa67e91d8f..2a0408ff556 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -103,7 +103,7 @@ export class TerminalPanel extends Panel { } this._updateFont(); this._updateTheme(); - if (!hasTerminals) { + if (hasTerminals) { this._terminalService.getActiveTab()?.setVisible(visible); } } From fae08be56b3f42343640898f959f1c11f02b6cf6 Mon Sep 17 00:00:00 2001 From: RealZogger <49367953+RealZogger@users.noreply.github.com> Date: Tue, 7 Jan 2020 21:37:44 +0000 Subject: [PATCH 060/843] Replace all description-container classes with correct selector A number of styles were missing from various locations due to the changes to support multiple icon labels in 8bb358f This change replaces all instances of the old class name .monaco-icon-label-description-container with the selector .monaco-icon-label-container > .monaco-icon-name-container --- src/vs/base/parts/quickopen/browser/quickopen.css | 2 +- src/vs/editor/contrib/suggest/media/suggest.css | 2 +- .../browser/parts/editor/media/tabstitlecontrol.css | 6 +++--- src/vs/workbench/browser/parts/views/media/views.css | 2 +- src/vs/workbench/contrib/remote/browser/remoteViewlet.css | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/base/parts/quickopen/browser/quickopen.css b/src/vs/base/parts/quickopen/browser/quickopen.css index b6a9b9247d0..c53b0fafe24 100644 --- a/src/vs/base/parts/quickopen/browser/quickopen.css +++ b/src/vs/base/parts/quickopen/browser/quickopen.css @@ -77,7 +77,7 @@ } .monaco-quick-open-widget .quick-open-tree .monaco-icon-label, -.monaco-quick-open-widget .quick-open-tree .monaco-icon-label .monaco-icon-label-description-container { +.monaco-quick-open-widget .quick-open-tree .monaco-icon-label .monaco-icon-label-container > .monaco-icon-name-container { flex: 1; /* make sure the icon label grows within the row */ } diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index 922fa8cb6e5..25eccec239d 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -162,7 +162,7 @@ opacity: 0.66; text-decoration: unset; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated > .monaco-icon-label-description-container { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated > .monaco-icon-label-container > .monaco-icon-name-container { text-decoration: line-through; } diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index cfde4395457..ef2f863d30e 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -159,15 +159,15 @@ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label > .monaco-icon-label-description-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label > .monaco-icon-label-container > .monaco-icon-name-container { overflow: visible; /* fixes https://github.com/Microsoft/vscode/issues/20182 */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container > .monaco-icon-name-container { text-overflow: clip; } -.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container { +.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container > .monaco-icon-name-container { text-overflow: ellipsis; } diff --git a/src/vs/workbench/browser/parts/views/media/views.css b/src/vs/workbench/browser/parts/views/media/views.css index 549d209a483..fd2fa438e9d 100644 --- a/src/vs/workbench/browser/parts/views/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -121,7 +121,7 @@ margin-top: 3px; } -.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .custom-view-tree-node-item-resourceLabel .monaco-icon-label-description-container { +.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .custom-view-tree-node-item-resourceLabel .monaco-icon-label-container > .monaco-icon-name-container { flex: 1; } diff --git a/src/vs/workbench/contrib/remote/browser/remoteViewlet.css b/src/vs/workbench/contrib/remote/browser/remoteViewlet.css index c1d72e03738..f9b9d5cc1b8 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteViewlet.css +++ b/src/vs/workbench/contrib/remote/browser/remoteViewlet.css @@ -29,7 +29,7 @@ width: 0px !important; } -.remote-help-tree-node-item-icon .monaco-icon-label-description-container { +.remote-help-tree-node-item-icon .monaco-icon-label-container > .monaco-icon-name-container { padding-left: 22px; } From fd936fb58556bc26313e58e12bc743b29a13f8c5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 8 Jan 2020 09:33:20 +0100 Subject: [PATCH 061/843] remove fs implementation --- .../bulkEdit/browser/bulkEditPreview.ts | 206 +----------------- 1 file changed, 2 insertions(+), 204 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index aee181049b0..a9e8050e2d2 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -9,14 +9,11 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { WorkspaceEdit, isResourceTextEdit, TextEdit } from 'vs/editor/common/modes'; -import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { mergeSort } from 'vs/base/common/arrays'; import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; -import * as files from 'vs/platform/files/common/files'; -import { Event, Emitter } from 'vs/base/common/event'; -import { TernarySearchTree, values } from 'vs/base/common/map'; -import { basename } from 'vs/base/common/resources'; +import { values } from 'vs/base/common/map'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export const enum BulkFileOperationType { @@ -180,202 +177,3 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider { return this._modelService.getModel(previewUri); } } - -export function asPreviewUri(uri: URI): URI { - return URI.from({ scheme: 'vscode-bulkedit-preview', path: uri.path, query: uri.toString() }); -} - -export function fromPreviewUri(uri: URI): URI { - return URI.parse(uri.query); -} - -class File implements files.IStat { - - type: files.FileType; - ctime: number; - mtime: number; - size: number; - - name: string; - data?: Uint8Array; - - constructor(name: string) { - this.type = files.FileType.File; - this.ctime = Date.now(); - this.mtime = Date.now(); - this.size = 0; - this.name = name; - } -} - -class Directory implements files.IStat { - - type: files.FileType; - ctime: number; - mtime: number; - size: number; - - name: string; - entries: Map; - - constructor(name: string) { - this.type = files.FileType.Directory; - this.ctime = Date.now(); - this.mtime = Date.now(); - this.size = 0; - this.name = name; - this.entries = new Map(); - } -} - -type Entry = File | Directory; - -export class BulkEditFileSystem implements files.IFileSystemProvider { - - readonly capabilities = files.FileSystemProviderCapabilities.FileReadWrite; - readonly onDidChangeCapabilities = Event.None; - - private readonly _registration: IDisposable; - private readonly _onDidChangeFile = new Emitter(); - readonly onDidChangeFile = this._onDidChangeFile.event; - - private readonly _entries = TernarySearchTree.forPaths(); - private readonly _deleted = TernarySearchTree.forPaths(); - - private _pendingChanges: files.IFileChange[] = []; - private _pendingHandle?: any; - - constructor(@files.IFileService private _fileService: files.IFileService) { - this._registration = _fileService.registerProvider('vscode-bulkedit-preview', this); - } - - dispose(): void { - this._registration.dispose(); - } - - private _fireSoon(event: files.IFileChange): void { - this._pendingChanges.push(event); - clearTimeout(this._pendingHandle); - this._pendingHandle = setTimeout(() => { - this._onDidChangeFile.fire(this._pendingChanges.slice(0)); - this._pendingChanges = []; - }, 0); - } - - private _checkDeleted(resource: URI): void { - if (this._deleted.findSubstr(resource.toString())) { - throw new Error('deleted ' + resource.toString()); - } - } - - watch(_resource: URI, _opts: files.IWatchOptions): IDisposable { - return Disposable.None; - } - - async stat(resource: URI): Promise { - this._checkDeleted(resource); - const entry = this._entries.get(resource.toString()); - if (entry) { - return entry; - } - - const stat = await this._fileService.resolve(fromPreviewUri(resource), { resolveMetadata: true }); - return { - type: (stat.isSymbolicLink ? files.FileType.SymbolicLink : 0) + (stat.isFile ? files.FileType.File : 0) + (stat.isDirectory ? files.FileType.Directory : 0), - ctime: stat.ctime, - mtime: stat.mtime, - size: stat.size, - }; - } - - async readdir(resource: URI): Promise<[string, files.FileType][]> { - // resource = fromPreviewUri(resource); - this._checkDeleted(resource); - - const entry = this._entries.get(resource.toString()); - const result: [string, files.FileType][] = []; - if (entry instanceof Directory) { - entry.entries.forEach(value => result.push([value.name, value.type])); - } - try { - const stat = await this._fileService.resolve(fromPreviewUri(resource), { resolveMetadata: true }); - if (stat.children) { - for (let child of stat.children) { - result.push([ - child.name, - (child.isSymbolicLink ? files.FileType.SymbolicLink : 0) + (child.isFile ? files.FileType.File : 0) + (child.isDirectory ? files.FileType.Directory : 0) - ]); - } - } - - - } catch { - // ignore - } - - return result; - } - - async mkdir(resource: URI): Promise { - // resource = fromPreviewUri(resource); - - const dir = new Directory(basename(resource)); - this._entries.set(resource.toString(), dir); - this._fireSoon({ resource, type: files.FileChangeType.ADDED }); - } - - async delete(resource: URI, _opts: files.FileDeleteOptions): Promise { - // resource = fromPreviewUri(resource); - this._deleted.set(resource.toString(), true); - } - - async rename(from: URI, to: URI, opts: files.FileOverwriteOptions): Promise { - // from = fromPreviewUri(from); - // to = fromPreviewUri(to); - - const target = new File(basename(to)); - target.type = (await this.stat(from)).type; - - this._deleted.set(from.toString(), true); - this._entries.set(to.toString(), target); - - // todo@joh copy files - // const iter = this._entries.findSuperstr(from.toString()); - // if (iter) { - // for (let next = iter.next(); !next.done; next = iter.next()) { - - // } - // } - // this._entries.delete(from.toString()); - - //todo@joh RENAME EVENT? - this._fireSoon({ resource: from, type: files.FileChangeType.DELETED }); - this._fireSoon({ resource: to, type: files.FileChangeType.ADDED }); - } - - // copy?(from: URI, to: URI, opts: FileOverwriteOptions): Promise; - - async readFile(resource: URI): Promise { - this._checkDeleted(resource); - - const entry = this._entries.get(resource.toString()); - if (entry instanceof File) { - return entry.data || new Uint8Array(); - } - return (await this._fileService.readFile(fromPreviewUri(resource))).value.buffer; - } - - async writeFile(resource: URI, content: Uint8Array, opts: files.FileWriteOptions): Promise { - - let entry = this._entries.get(resource.toString()); - if (!entry && opts.create) { - entry = new File(basename(resource)); - this._entries.set(resource.toString(), entry); - } - if (!(entry instanceof File)) { - throw new Error(); - } - entry.data = content; - this._fireSoon({ resource, type: files.FileChangeType.UPDATED }); - } -} From bfe4acac71f5c7034b5f20d56f19fb7f5fcbb7d1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 8 Jan 2020 09:41:40 +0100 Subject: [PATCH 062/843] simplify IdentProvider, rename view model object --- .../contrib/bulkEdit/browser/bulkEditPanel.ts | 4 +-- .../contrib/bulkEdit/browser/bulkEditTree.ts | 28 ++++++++----------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts index 82a1eb77df1..45593fb8558 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts @@ -8,7 +8,7 @@ import { Panel } from 'vs/workbench/browser/panel'; import { Dimension } from 'vs/base/browser/dom'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; -import { Edit, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; +import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -33,7 +33,7 @@ export class BulkEditPanel extends Panel { static readonly ID = 'BulkEditPanel'; - private _tree!: WorkbenchAsyncDataTree; + private _tree!: WorkbenchAsyncDataTree; private _message!: HTMLSpanElement; private readonly _acceptAction = new Action('ok', localize('ok', "Apply Refactoring"), 'codicon-check', false, async () => this._done(true)); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 9b0eb46c031..633aed1e58b 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -43,15 +43,15 @@ export class TextEditElement { ) { } } -export type Edit = FileElement | TextEditElement; +export type BulkEditElement = FileElement | TextEditElement; // --- DATA SOURCE -export class BulkEditDataSource implements IAsyncDataSource { +export class BulkEditDataSource implements IAsyncDataSource { constructor(@ITextModelService private readonly _textModelService: ITextModelService) { } - hasChildren(element: BulkFileOperations | Edit): boolean { + hasChildren(element: BulkFileOperations | BulkEditElement): boolean { if (element instanceof FileElement) { return element.edit.textEdits.length > 0; } @@ -61,7 +61,7 @@ export class BulkEditDataSource implements IAsyncDataSource { + async getChildren(element: BulkFileOperations | BulkEditElement): Promise { // root -> file/text edits if (element instanceof BulkFileOperations) { @@ -111,18 +111,14 @@ export class BulkEditDataSource implements IAsyncDataSource { +export class BulkEditIdentityProvider implements IIdentityProvider { - private readonly _map = new WeakMap(); - private _idPool = 0; - - getId(element: Edit): { toString(): string; } { - let id = this._map.get(element); - if (typeof id === 'undefined') { - id = this._idPool++; - this._map.set(element, id); + getId(element: BulkEditElement): { toString(): string; } { + if (element instanceof FileElement) { + return element.uri; + } else { + return element.parent.uri.toString() + JSON.stringify(element.edit); } - return id; } } @@ -204,13 +200,13 @@ export class TextEditElementRenderer implements ITreeRenderer { +export class BulkEditDelegate implements IListVirtualDelegate { getHeight(): number { return 23; } - getTemplateId(element: Edit): string { + getTemplateId(element: BulkEditElement): string { return element instanceof FileElement ? FileElementRenderer.id : TextEditElementRenderer.id; From 53ae4c8aaf65a8ef34b266647e4acb95ea6f5999 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 8 Jan 2020 09:47:47 +0100 Subject: [PATCH 063/843] show file operation labels with file name --- .../contrib/bulkEdit/browser/bulkEditTree.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 633aed1e58b..f0435370456 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -18,7 +18,8 @@ import { ITextModel } from 'vs/editor/common/model'; import { IDisposable } from 'vs/base/common/lifecycle'; import { TextModel } from 'vs/editor/common/model/textModel'; import { ILabelService } from 'vs/platform/label/common/label'; -import { BulkFileOperations, BulkFileOperation } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { BulkFileOperations, BulkFileOperation, BulkFileOperationType } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { localize } from 'vs/nls'; // --- VIEW MODEL @@ -153,9 +154,20 @@ export class FileElementRenderer implements ITreeRenderer, _index: number, template: FileElementTemplate): void { + let fileOperationsLabels: string[] = []; + if (node.element.edit.type & BulkFileOperationType.Create) { + fileOperationsLabels.push(localize('file.create', "create file")); + } + if (node.element.edit.type & BulkFileOperationType.Delete) { + fileOperationsLabels.push(localize('file.delete', "delete file")); + } + if (node.element.edit.type & BulkFileOperationType.Rename) { + fileOperationsLabels.push(localize('file.rename', "rename file")); + } + template.label.setResource({ name: this._labelService.getUriLabel(node.element.uri, { relative: true }), - description: node.element._debugName, + description: fileOperationsLabels.join(', '), resource: node.element.uri, }, { matches: createMatches(node.filterData), From bb799f2bc7c3deb2a77b89c36f6aab53d40e8731 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 8 Jan 2020 10:07:52 +0100 Subject: [PATCH 064/843] filter file noops --- .../bulkEdit/browser/bulkEditPreview.ts | 21 +++++++++++---- .../contrib/bulkEdit/browser/bulkEditTree.ts | 27 +++++++++---------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index a9e8050e2d2..654ad0d8361 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -15,6 +15,7 @@ import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { values } from 'vs/base/common/map'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IFileService } from 'vs/platform/files/common/files'; export const enum BulkFileOperationType { None = 0, @@ -41,10 +42,12 @@ export class BulkFileOperation { export class BulkFileOperations { - static async create(_accessor: ServicesAccessor, bulkEdit: WorkspaceEdit): Promise { + static async create(accessor: ServicesAccessor, bulkEdit: WorkspaceEdit): Promise { const operationByResource = new Map(); + const fileService = accessor.get(IFileService); + for (const edit of bulkEdit.edits) { let uri: URI; @@ -55,25 +58,35 @@ export class BulkFileOperations { type = BulkFileOperationType.None; uri = edit.resource; textEdits = edit.edits; - } else if (edit.newUri && edit.oldUri) { type = BulkFileOperationType.Rename; uri = edit.oldUri; + if (edit.options?.overwrite === undefined && edit.options?.ignoreIfExists && await fileService.exists(uri)) { + // noop -> "soft" rename to something that already exists + continue; + } } else if (edit.oldUri) { type = BulkFileOperationType.Delete; uri = edit.oldUri; + if (edit.options?.ignoreIfNotExists && !await fileService.exists(uri)) { + // noop -> "soft" delete something that doesn't exist + continue; + } } else if (edit.newUri) { type = BulkFileOperationType.Create; uri = edit.newUri; + if (edit.options?.overwrite === undefined && edit.options?.ignoreIfExists && await fileService.exists(uri)) { + // noop -> "soft" create something that already exists + continue; + } } else { // invalid edit -> skip continue; } - const key = uri.toString(); let operation = operationByResource.get(key); if (!operation) { @@ -87,8 +100,6 @@ export class BulkFileOperations { } } - //todo@joh filter noops - return new BulkFileOperations(values(operationByResource)); } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index f0435370456..13fa844ffb5 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -25,12 +25,22 @@ import { localize } from 'vs/nls'; export class FileElement { + private static _typeLabels: Record = { + [BulkFileOperationType.Create]: localize('create', "create"), + [BulkFileOperationType.Delete]: localize('delete', "delete"), + [BulkFileOperationType.Rename]: localize('rename', "rename"), + [BulkFileOperationType.Create | BulkFileOperationType.Delete]: localize('createDelete', "create & delete"), + [BulkFileOperationType.Create | BulkFileOperationType.Rename]: localize('createRename', "create & rename"), + [BulkFileOperationType.Delete | BulkFileOperationType.Rename]: localize('deleteRename', "delete & rename"), + [BulkFileOperationType.Create | BulkFileOperationType.Delete | BulkFileOperationType.Rename]: localize('createRenameDelete', "create, rename, delete"), + }; + readonly uri: URI; - readonly _debugName: string; + readonly typeLabel: string; constructor(readonly edit: BulkFileOperation) { this.uri = edit.uri; - this._debugName = `0b${edit.type.toString(2)}`; + this.typeLabel = FileElement._typeLabels[edit.type] || ''; } } @@ -154,20 +164,9 @@ export class FileElementRenderer implements ITreeRenderer, _index: number, template: FileElementTemplate): void { - let fileOperationsLabels: string[] = []; - if (node.element.edit.type & BulkFileOperationType.Create) { - fileOperationsLabels.push(localize('file.create', "create file")); - } - if (node.element.edit.type & BulkFileOperationType.Delete) { - fileOperationsLabels.push(localize('file.delete', "delete file")); - } - if (node.element.edit.type & BulkFileOperationType.Rename) { - fileOperationsLabels.push(localize('file.rename', "rename file")); - } - template.label.setResource({ name: this._labelService.getUriLabel(node.element.uri, { relative: true }), - description: fileOperationsLabels.join(', '), + description: node.element.typeLabel, resource: node.element.uri, }, { matches: createMatches(node.filterData), From 1e2ca4dbddc58aacae0900276bed07685ee4fdbd Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 8 Jan 2020 12:16:10 +0100 Subject: [PATCH 065/843] Have the rendering width of a tab character be independent of breaking points --- .../browser/viewParts/lines/viewLine.ts | 1 + .../editor/browser/widget/diffEditorWidget.ts | 1 + src/vs/editor/browser/widget/diffReview.ts | 1 + .../common/viewLayout/viewLineRenderer.ts | 102 ++++++------- .../characterHardWrappingLineMapper.ts | 136 +++++++----------- .../common/viewModel/splitLinesCollection.ts | 45 ++++-- src/vs/editor/common/viewModel/viewModel.ts | 14 +- .../editor/common/viewModel/viewModelImpl.ts | 3 +- src/vs/editor/standalone/browser/colorizer.ts | 3 + .../viewLayout/viewLineRenderer.test.ts | 31 ++++ .../characterHardWrappingLineMapper.test.ts | 4 +- .../viewModel/splitLinesCollection.test.ts | 96 ++++++------- 12 files changed, 235 insertions(+), 202 deletions(-) diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 8840a014032..cbe9a08f45f 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -213,6 +213,7 @@ export class ViewLine implements IVisibleLine { lineData.tokens, actualInlineDecorations, lineData.tabSize, + lineData.startVisibleColumn, options.spaceWidth, options.stopRenderingLineAfter, options.renderWhitespace, diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 91b91732033..e6ae832954a 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -2144,6 +2144,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { lineTokens, actualDecorations, tabSize, + 0, fontInfo.spaceWidth, options.get(EditorOption.stopRenderingLineAfter), options.get(EditorOption.renderWhitespace), diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index b0437b6d47f..4ac1919a690 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -780,6 +780,7 @@ export class DiffReview extends Disposable { lineTokens, [], tabSize, + 0, fontInfo.spaceWidth, options.get(EditorOption.stopRenderingLineAfter), options.get(EditorOption.renderWhitespace), diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index 5298d234529..fec4ba1dfb3 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -66,6 +66,7 @@ export class RenderLineInput { public readonly lineTokens: IViewLineTokens; public readonly lineDecorations: LineDecoration[]; public readonly tabSize: number; + public readonly startVisibleColumn: number; public readonly spaceWidth: number; public readonly stopRenderingLineAfter: number; public readonly renderWhitespace: RenderWhitespace; @@ -89,6 +90,7 @@ export class RenderLineInput { lineTokens: IViewLineTokens, lineDecorations: LineDecoration[], tabSize: number, + startVisibleColumn: number, spaceWidth: number, stopRenderingLineAfter: number, renderWhitespace: 'none' | 'boundary' | 'selection' | 'all', @@ -106,6 +108,7 @@ export class RenderLineInput { this.lineTokens = lineTokens; this.lineDecorations = lineDecorations; this.tabSize = tabSize; + this.startVisibleColumn = startVisibleColumn; this.spaceWidth = spaceWidth; this.stopRenderingLineAfter = stopRenderingLineAfter; this.renderWhitespace = ( @@ -154,6 +157,7 @@ export class RenderLineInput { && this.containsRTL === other.containsRTL && this.fauxIndentLength === other.fauxIndentLength && this.tabSize === other.tabSize + && this.startVisibleColumn === other.startVisibleColumn && this.spaceWidth === other.spaceWidth && this.stopRenderingLineAfter === other.stopRenderingLineAfter && this.renderWhitespace === other.renderWhitespace @@ -368,7 +372,9 @@ class ResolvedRenderLineInput { public readonly isOverflowing: boolean, public readonly parts: LinePart[], public readonly containsForeignElements: ForeignElementType, + public readonly fauxIndentLength: number, public readonly tabSize: number, + public readonly startVisibleColumn: number, public readonly containsRTL: boolean, public readonly spaceWidth: number, public readonly renderWhitespace: RenderWhitespace, @@ -395,7 +401,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len); if (input.renderWhitespace === RenderWhitespace.All || input.renderWhitespace === RenderWhitespace.Boundary || (input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine)) { - tokens = _applyRenderWhitespace(lineContent, len, input.continuesWithWrappedLine, tokens, input.fauxIndentLength, input.tabSize, useMonospaceOptimizations, input.selectionsOnLine, input.renderWhitespace === RenderWhitespace.Boundary); + tokens = _applyRenderWhitespace(lineContent, len, input.continuesWithWrappedLine, tokens, input.fauxIndentLength, input.tabSize, input.startVisibleColumn, useMonospaceOptimizations, input.selectionsOnLine, input.renderWhitespace === RenderWhitespace.Boundary); } let containsForeignElements = ForeignElementType.None; if (input.lineDecorations.length > 0) { @@ -425,7 +431,9 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput isOverflowing, tokens, containsForeignElements, + input.fauxIndentLength, input.tabSize, + input.startVisibleColumn, input.containsRTL, input.spaceWidth, input.renderWhitespace, @@ -537,7 +545,7 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces: * Moreover, a token is created for every visual indent because on some fonts the glyphs used for rendering whitespace (→ or ·) do not have the same width as  . * The rendering phase will generate `style="width:..."` for these tokens. */ -function _applyRenderWhitespace(lineContent: string, len: number, continuesWithWrappedLine: boolean, tokens: LinePart[], fauxIndentLength: number, tabSize: number, useMonospaceOptimizations: boolean, selections: LineRange[] | null, onlyBoundary: boolean): LinePart[] { +function _applyRenderWhitespace(lineContent: string, len: number, continuesWithWrappedLine: boolean, tokens: LinePart[], fauxIndentLength: number, tabSize: number, startVisibleColumn: number, useMonospaceOptimizations: boolean, selections: LineRange[] | null, onlyBoundary: boolean): LinePart[] { let result: LinePart[] = [], resultLen = 0; let tokenIndex = 0; @@ -555,21 +563,10 @@ function _applyRenderWhitespace(lineContent: string, len: number, continuesWithW lastNonWhitespaceIndex = strings.lastNonWhitespaceIndex(lineContent); } - let tmpIndent = 0; - for (let charIndex = 0; charIndex < fauxIndentLength; charIndex++) { - const chCode = lineContent.charCodeAt(charIndex); - if (chCode === CharCode.Tab) { - tmpIndent = tabSize; - } else if (strings.isFullWidthCharacter(chCode)) { - tmpIndent += 2; - } else { - tmpIndent++; - } - } - tmpIndent = tmpIndent % tabSize; let wasInWhitespace = false; let currentSelectionIndex = 0; let currentSelection = selections && selections[currentSelectionIndex]; + let tmpIndent = startVisibleColumn % tabSize; for (let charIndex = fauxIndentLength; charIndex < len; charIndex++) { const chCode = lineContent.charCodeAt(charIndex); @@ -729,7 +726,9 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render const len = input.len; const isOverflowing = input.isOverflowing; const parts = input.parts; + const fauxIndentLength = input.fauxIndentLength; const tabSize = input.tabSize; + const startVisibleColumn = input.startVisibleColumn; const containsRTL = input.containsRTL; const spaceWidth = input.spaceWidth; const renderWhitespace = input.renderWhitespace; @@ -738,7 +737,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render const characterMapping = new CharacterMapping(len + 1, parts.length); let charIndex = 0; - let tabsCharDelta = 0; + let visibleColumn = startVisibleColumn; let charOffsetInPart = 0; let prevPartContentCnt = 0; @@ -764,18 +763,14 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render let partContentCnt = 0; { let _charIndex = charIndex; - let _tabsCharDelta = tabsCharDelta; + let _visibleColumn = visibleColumn; for (; _charIndex < partEndIndex; _charIndex++) { const charCode = lineContent.charCodeAt(_charIndex); - - if (charCode === CharCode.Tab) { - let insertSpacesCount = tabSize - (_charIndex + _tabsCharDelta) % tabSize; - _tabsCharDelta += insertSpacesCount - 1; - partContentCnt += insertSpacesCount; - } else { - // must be CharCode.Space - partContentCnt++; + const charWidth = (charCode === CharCode.Tab ? (tabSize - (_visibleColumn % tabSize)) : 1) | 0; + partContentCnt += charWidth; + if (_charIndex >= fauxIndentLength) { + _visibleColumn += charWidth; } } } @@ -793,29 +788,30 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render for (; charIndex < partEndIndex; charIndex++) { characterMapping.setPartData(charIndex, partIndex, charOffsetInPart, partAbsoluteOffset); const charCode = lineContent.charCodeAt(charIndex); + let charWidth: number; if (charCode === CharCode.Tab) { - let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize; - tabsCharDelta += insertSpacesCount - 1; - charOffsetInPart += insertSpacesCount - 1; - if (insertSpacesCount > 0) { - if (!canUseHalfwidthRightwardsArrow || insertSpacesCount > 1) { - sb.write1(0x2192); // RIGHTWARDS ARROW - } else { - sb.write1(0xFFEB); // HALFWIDTH RIGHTWARDS ARROW - } - insertSpacesCount--; + charWidth = (tabSize - (visibleColumn % tabSize)) | 0; + + if (!canUseHalfwidthRightwardsArrow || charWidth > 1) { + sb.write1(0x2192); // RIGHTWARDS ARROW + } else { + sb.write1(0xFFEB); // HALFWIDTH RIGHTWARDS ARROW } - while (insertSpacesCount > 0) { + for (let space = 2; space <= charWidth; space++) { sb.write1(0xA0); //   - insertSpacesCount--; } - } else { - // must be CharCode.Space + + } else { // must be CharCode.Space + charWidth = 1; + sb.write1(0xB7); // · } - charOffsetInPart++; + charOffsetInPart += charWidth; + if (charIndex >= fauxIndentLength) { + visibleColumn += charWidth; + } } prevPartContentCnt = partContentCnt; @@ -833,63 +829,59 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render characterMapping.setPartData(charIndex, partIndex, charOffsetInPart, partAbsoluteOffset); const charCode = lineContent.charCodeAt(charIndex); + let producedCharacters = 1; + let charWidth = 1; + switch (charCode) { case CharCode.Tab: - let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize; - tabsCharDelta += insertSpacesCount - 1; - charOffsetInPart += insertSpacesCount - 1; - while (insertSpacesCount > 0) { + producedCharacters = (tabSize - (visibleColumn % tabSize)); + charWidth = producedCharacters; + for (let space = 1; space <= producedCharacters; space++) { sb.write1(0xA0); //   - partContentCnt++; - insertSpacesCount--; } break; case CharCode.Space: sb.write1(0xA0); //   - partContentCnt++; break; case CharCode.LessThan: sb.appendASCIIString('<'); - partContentCnt++; break; case CharCode.GreaterThan: sb.appendASCIIString('>'); - partContentCnt++; break; case CharCode.Ampersand: sb.appendASCIIString('&'); - partContentCnt++; break; case CharCode.Null: sb.appendASCIIString('�'); - partContentCnt++; break; case CharCode.UTF8_BOM: case CharCode.LINE_SEPARATOR_2028: sb.write1(0xFFFD); - partContentCnt++; break; default: if (strings.isFullWidthCharacter(charCode)) { - tabsCharDelta++; + charWidth++; } if (renderControlCharacters && charCode < 32) { sb.write1(9216 + charCode); - partContentCnt++; } else { sb.write1(charCode); - partContentCnt++; } } - charOffsetInPart++; + charOffsetInPart += producedCharacters; + partContentCnt += producedCharacters; + if (charIndex >= fauxIndentLength) { + visibleColumn += charWidth; + } } prevPartContentCnt = partContentCnt; diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 5f4bc7f1828..e1fff8b193e 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -66,19 +66,11 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor this.classifier = new WrappingCharacterClassifier(breakBeforeChars, breakAfterChars); } - // TODO@Alex -> duplicated in lineCommentCommand - private static nextVisibleColumn(currentVisibleColumn: number, tabSize: number, isTab: boolean, columnSize: number): number { - currentVisibleColumn = +currentVisibleColumn; //@perf - tabSize = +tabSize; //@perf - columnSize = +columnSize; //@perf - - if (isTab) { - return currentVisibleColumn + (tabSize - (currentVisibleColumn % tabSize)); - } - return currentVisibleColumn + columnSize; - } - public createLineMappingComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineMappingComputer { + tabSize = tabSize | 0; //@perf + wrappingColumn = +wrappingColumn; //@perf + columnsForFullWidthChar = +columnsForFullWidthChar; //@perf + let requests: string[] = []; return { addRequest: (lineText: string) => { @@ -94,70 +86,57 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor }; } - private _createLineMapping(lineText: string, tabSize: number, breakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { - if (breakingColumn === -1) { + private _createLineMapping(lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { + if (firstLineBreakingColumn === -1) { return null; } - tabSize = +tabSize; //@perf - breakingColumn = +breakingColumn; //@perf - columnsForFullWidthChar = +columnsForFullWidthChar; //@perf - hardWrappingIndent = +hardWrappingIndent; //@perf - - let wrappedTextIndentVisibleColumn = 0; - let wrappedTextIndent = ''; - let firstNonWhitespaceIndex = -1; + let wrappedTextIndentLength = 0; if (hardWrappingIndent !== WrappingIndent.None) { firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineText); if (firstNonWhitespaceIndex !== -1) { // Track existing indent - wrappedTextIndent = lineText.substring(0, firstNonWhitespaceIndex); + for (let i = 0; i < firstNonWhitespaceIndex; i++) { - wrappedTextIndentVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(wrappedTextIndentVisibleColumn, tabSize, lineText.charCodeAt(i) === CharCode.Tab, 1); + const charWidth = (lineText.charCodeAt(i) === CharCode.Tab ? tabCharacterWidth(wrappedTextIndentLength, tabSize) : 1); + wrappedTextIndentLength += charWidth; } // Increase indent of continuation lines, if desired - let numberOfAdditionalTabs = 0; - if (hardWrappingIndent === WrappingIndent.Indent) { - numberOfAdditionalTabs = 1; - } else if (hardWrappingIndent === WrappingIndent.DeepIndent) { - numberOfAdditionalTabs = 2; - } + const numberOfAdditionalTabs = (hardWrappingIndent === WrappingIndent.DeepIndent ? 2 : hardWrappingIndent === WrappingIndent.Indent ? 1 : 0); for (let i = 0; i < numberOfAdditionalTabs; i++) { - wrappedTextIndent += '\t'; - wrappedTextIndentVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(wrappedTextIndentVisibleColumn, tabSize, true, 1); + const charWidth = tabCharacterWidth(wrappedTextIndentLength, tabSize); + wrappedTextIndentLength += charWidth; } // Force sticking to beginning of line if no character would fit except for the indentation - if (wrappedTextIndentVisibleColumn + columnsForFullWidthChar > breakingColumn) { - wrappedTextIndent = ''; - wrappedTextIndentVisibleColumn = 0; + if (wrappedTextIndentLength + columnsForFullWidthChar > firstLineBreakingColumn) { + wrappedTextIndentLength = 0; } } } + const wrappedLineBreakingColumn = firstLineBreakingColumn - wrappedTextIndentLength; const classifier = this.classifier; - let breakingOffsets: number[] = []; // The offset where a break occurs - let breakingOffsetsIndex: number = 0; // The count of breaks already done - let visibleColumn = 0; // Visible column since the beginning of the current line - let niceBreakOffset = 0; // Last index of a character that indicates a break should happen before it (more desirable) - let niceBreakVisibleColumn = 0; // visible column if a break were to be later introduced before `niceBreakOffset` + let breakingOffsets: number[] = []; + let breakingOffsetsVisibleColumn: number[] = []; + let breakingOffsetsCount: number = 0; + let visibleColumn = 0; + let breakOffset = 0; + let breakOffsetVisibleColumn = 0; const len = lineText.length; + let breakingColumn = firstLineBreakingColumn; for (let i = 0; i < len; i++) { // At this point, there is a certainty that the character before `i` fits on the current line, // but the character at `i` might not fit const charCode = lineText.charCodeAt(i); + if (strings.isLowSurrogate(charCode)) { // A surrogate pair must always be considered as a single unit, so it is never to be broken - - // Advance visibleColumn & niceBreakVisibleColumn - visibleColumn = visibleColumn + 1; - if (niceBreakOffset !== 0) { - niceBreakVisibleColumn = niceBreakVisibleColumn + 1; - } + visibleColumn += 1; continue; } @@ -171,68 +150,59 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor // (or) CJK breaking : before break : Kinsoku Shori : Don't break after a leading character, like an open bracket // Since we are certain the character before `i` fits, there's no extra checking needed, // just mark it as a nice breaking opportunity - niceBreakOffset = i; - niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; + breakOffset = i; + breakOffsetVisibleColumn = visibleColumn; } - const charCodeIsTab = (charCode === CharCode.Tab); - const charColumnSize = strings.isFullWidthCharacter(charCode) ? columnsForFullWidthChar : 1; + const charColumnSize = ( + charCode === CharCode.Tab + ? tabCharacterWidth(visibleColumn, tabSize) + : (strings.isFullWidthCharacter(charCode) ? columnsForFullWidthChar : 1) + ); - // Advance visibleColumn with character at `i` - visibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(visibleColumn, tabSize, charCodeIsTab, charColumnSize); + visibleColumn += charColumnSize; + // check if adding character at `i` will go over the breaking column if (visibleColumn > breakingColumn && i !== 0) { // We need to break at least before character at `i`: - // - break before niceBreakLastOffset if it exists (and re-establish a correct visibleColumn by using niceBreakVisibleColumn + charAt(i)) - // - otherwise, break before obtrusiveBreakLastOffset if it exists (and re-establish a correct visibleColumn by using obtrusiveBreakVisibleColumn + charAt(i)) - // - otherwise, break before i (and re-establish a correct visibleColumn by charAt(i)) - - if (niceBreakOffset !== 0 && niceBreakVisibleColumn <= breakingColumn) { - - // We will break before `niceBreakLastOffset` - breakingOffsets[breakingOffsetsIndex++] = niceBreakOffset; - visibleColumn = niceBreakVisibleColumn; - - } else { - - // We will break before `i` - breakingOffsets[breakingOffsetsIndex++] = i; - visibleColumn = wrappedTextIndentVisibleColumn; + if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { + // Cannot break at `breakOffset`, must break at `i` + breakOffset = i; + breakOffsetVisibleColumn = visibleColumn - charColumnSize; } - // Re-establish visibleColumn by taking character at `i` into account - visibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(visibleColumn, tabSize, charCodeIsTab, charColumnSize); - - // Reset markers - niceBreakOffset = 0; + breakingOffsets[breakingOffsetsCount] = breakOffset; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; + breakingOffsetsCount++; + breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; + breakOffset = 0; } // At this point, there is a certainty that the character at `i` fits on the current line - - if (niceBreakOffset !== 0) { - // Advance niceBreakVisibleColumn - niceBreakVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(niceBreakVisibleColumn, tabSize, charCodeIsTab, charColumnSize); - } - if ( (charCodeClass === CharacterClass.BREAK_AFTER && (hardWrappingIndent === WrappingIndent.None || i >= firstNonWhitespaceIndex)) || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i + 1 < len && classifier.get(lineText.charCodeAt(i + 1)) !== CharacterClass.BREAK_AFTER) ) { // This is a character that indicates that a break should happen after it // (or) CJK breaking : after break : Kinsoku Shori : Don't break before a trailing character, like a period - niceBreakOffset = i + 1; - niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; + breakOffset = i + 1; + breakOffsetVisibleColumn = visibleColumn; } } - if (breakingOffsetsIndex === 0) { + if (breakingOffsetsCount === 0) { return null; } // Add last segment - breakingOffsets[breakingOffsetsIndex++] = len; + breakingOffsets[breakingOffsetsCount] = len; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = visibleColumn; - return new LineBreakingData(breakingOffsets, wrappedTextIndent); + return new LineBreakingData(breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); } } + +function tabCharacterWidth(visibleColumn: number, tabSize: number): number { + return (tabSize - (visibleColumn % tabSize)); +} diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index f90d1dd6372..113059661f9 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -29,7 +29,8 @@ export class OutputPosition { export class LineBreakingData { constructor( public readonly breakOffsets: number[], - public readonly wrappedLinesIndent: string + public readonly breakingOffsetsVisibleColumn: number[], + public readonly wrappedTextIndentLength: number ) { } public static getInputOffsetOfOutputPosition(breakOffsets: number[], outputLineIndex: number, outputOffset: number): number { @@ -1032,6 +1033,7 @@ class VisibleIdentitySplitLine implements ISplitLine { false, 1, lineContent.length + 1, + 0, lineTokens.inflate() ); } @@ -1118,12 +1120,14 @@ class InvisibleIdentitySplitLine implements ISplitLine { export class SplitLine implements ISplitLine { private readonly _breakOffsets: number[]; - private readonly _wrappedIndent: string; + private readonly _breakingOffsetsVisibleColumn: number[]; + private readonly _wrappedTextIndentLength: number; private _isVisible: boolean; constructor(lineBreaking: LineBreakingData, isVisible: boolean) { this._breakOffsets = lineBreaking.breakOffsets; - this._wrappedIndent = lineBreaking.wrappedLinesIndent; + this._breakingOffsetsVisibleColumn = lineBreaking.breakingOffsetsVisibleColumn; + this._wrappedTextIndentLength = lineBreaking.wrappedTextIndentLength; this._isVisible = isVisible; } @@ -1168,7 +1172,7 @@ export class SplitLine implements ISplitLine { }); if (outputLineIndex > 0) { - r = this._wrappedIndent + r; + r = spaces(this._wrappedTextIndentLength) + r; } return r; @@ -1183,7 +1187,7 @@ export class SplitLine implements ISplitLine { let r = endOffset - startOffset; if (outputLineIndex > 0) { - r = this._wrappedIndent.length + r; + r = this._wrappedTextIndentLength + r; } return r; @@ -1194,7 +1198,7 @@ export class SplitLine implements ISplitLine { throw new Error('Not supported'); } if (outputLineIndex > 0) { - return this._wrappedIndent.length + 1; + return this._wrappedTextIndentLength + 1; } return 1; } @@ -1222,25 +1226,28 @@ export class SplitLine implements ISplitLine { }); if (outputLineIndex > 0) { - lineContent = this._wrappedIndent + lineContent; + lineContent = spaces(this._wrappedTextIndentLength) + lineContent; } - let minColumn = (outputLineIndex > 0 ? this._wrappedIndent.length + 1 : 1); + let minColumn = (outputLineIndex > 0 ? this._wrappedTextIndentLength + 1 : 1); let maxColumn = lineContent.length + 1; let continuesWithWrappedLine = (outputLineIndex + 1 < this.getViewLineCount()); let deltaStartIndex = 0; if (outputLineIndex > 0) { - deltaStartIndex = this._wrappedIndent.length; + deltaStartIndex = this._wrappedTextIndentLength; } let lineTokens = model.getLineTokens(modelLineNumber); + const startVisibleColumn = (outputLineIndex === 0 ? 0 : this._breakingOffsetsVisibleColumn[outputLineIndex - 1]); + return new ViewLineData( lineContent, continuesWithWrappedLine, minColumn, maxColumn, + startVisibleColumn, lineTokens.sliceAndInflate(startOffset, endOffset, deltaStartIndex) ); } @@ -1266,10 +1273,10 @@ export class SplitLine implements ISplitLine { } let adjustedColumn = outputColumn - 1; if (outputLineIndex > 0) { - if (adjustedColumn < this._wrappedIndent.length) { + if (adjustedColumn < this._wrappedTextIndentLength) { adjustedColumn = 0; } else { - adjustedColumn -= this._wrappedIndent.length; + adjustedColumn -= this._wrappedTextIndentLength; } } return LineBreakingData.getInputOffsetOfOutputPosition(this._breakOffsets, outputLineIndex, adjustedColumn) + 1; @@ -1284,7 +1291,7 @@ export class SplitLine implements ISplitLine { let outputColumn = r.outputOffset + 1; if (outputLineIndex > 0) { - outputColumn += this._wrappedIndent.length; + outputColumn += this._wrappedTextIndentLength; } // console.log('in -> out ' + deltaLineNumber + ',' + inputColumn + ' ===> ' + (deltaLineNumber+outputLineIndex) + ',' + outputColumn); @@ -1300,6 +1307,19 @@ export class SplitLine implements ISplitLine { } } +let _spaces: string[] = ['']; +function spaces(count: number): string { + if (count >= _spaces.length) { + for (let i = 1; i <= count; i++) { + _spaces[i] = _makeSpaces(i); + } + } + return _spaces[count]; +} +function _makeSpaces(count: number): string { + return new Array(count + 1).join(' '); +} + function createSplitLine(lineMapping: LineBreakingData | null, isVisible: boolean): ISplitLine { if (lineMapping === null) { // No mapping needed @@ -1473,6 +1493,7 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { false, 1, lineContent.length + 1, + 0, lineTokens.inflate() ); } diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index a3abd74ee4e..a79c030bf96 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -174,6 +174,10 @@ export class ViewLineData { * The maximum allowed column at this view line. */ public readonly maxColumn: number; + /** + * The visible column at the start of the line (after the fauxIndent). + */ + public readonly startVisibleColumn: number; /** * The tokens at this view line. */ @@ -184,12 +188,14 @@ export class ViewLineData { continuesWithWrappedLine: boolean, minColumn: number, maxColumn: number, + startVisibleColumn: number, tokens: IViewLineTokens ) { this.content = content; this.continuesWithWrappedLine = continuesWithWrappedLine; this.minColumn = minColumn; this.maxColumn = maxColumn; + this.startVisibleColumn = startVisibleColumn; this.tokens = tokens; } } @@ -231,6 +237,10 @@ export class ViewLineRenderingData { * The tab size for this view model. */ public readonly tabSize: number; + /** + * The visible column at the start of the line (after the fauxIndent) + */ + public readonly startVisibleColumn: number; constructor( minColumn: number, @@ -241,7 +251,8 @@ export class ViewLineRenderingData { mightContainNonBasicASCII: boolean, tokens: IViewLineTokens, inlineDecorations: InlineDecoration[], - tabSize: number + tabSize: number, + startVisibleColumn: number ) { this.minColumn = minColumn; this.maxColumn = maxColumn; @@ -254,6 +265,7 @@ export class ViewLineRenderingData { this.tokens = tokens; this.inlineDecorations = inlineDecorations; this.tabSize = tabSize; + this.startVisibleColumn = startVisibleColumn; } public static isBasicASCII(lineContent: string, mightContainNonBasicASCII: boolean): boolean { diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 229df6e3614..f437ceeab26 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -564,7 +564,8 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel mightContainNonBasicASCII, lineData.tokens, inlineDecorations, - tabSize + tabSize, + lineData.startVisibleColumn ); } diff --git a/src/vs/editor/standalone/browser/colorizer.ts b/src/vs/editor/standalone/browser/colorizer.ts index d72d141046b..679734f8652 100644 --- a/src/vs/editor/standalone/browser/colorizer.ts +++ b/src/vs/editor/standalone/browser/colorizer.ts @@ -125,6 +125,7 @@ export class Colorizer { [], tabSize, 0, + 0, -1, 'none', false, @@ -193,6 +194,7 @@ function _fakeColorize(lines: string[], tabSize: number): string { [], tabSize, 0, + 0, -1, 'none', false, @@ -230,6 +232,7 @@ function _actualColorize(lines: string[], tabSize: number, tokenizationSupport: [], tabSize, 0, + 0, -1, 'none', false, diff --git a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts index 040629e325b..ccb50958ccd 100644 --- a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts +++ b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts @@ -38,6 +38,7 @@ suite('viewLineRenderer.renderLine', () => { [], tabSize, 0, + 0, -1, 'none', false, @@ -88,6 +89,7 @@ suite('viewLineRenderer.renderLine', () => { [], tabSize, 0, + 0, -1, 'none', false, @@ -140,6 +142,7 @@ suite('viewLineRenderer.renderLine', () => { ]), [], 4, + 0, 10, 6, 'boundary', @@ -232,6 +235,7 @@ suite('viewLineRenderer.renderLine', () => { lineParts, [], 4, + 0, 10, -1, 'boundary', @@ -295,6 +299,7 @@ suite('viewLineRenderer.renderLine', () => { lineParts, [], 4, + 0, 10, -1, 'none', @@ -358,6 +363,7 @@ suite('viewLineRenderer.renderLine', () => { lineParts, [], 4, + 0, 10, -1, 'none', @@ -398,6 +404,7 @@ suite('viewLineRenderer.renderLine', () => { lineParts, [], 4, + 0, 10, -1, 'none', @@ -429,6 +436,7 @@ suite('viewLineRenderer.renderLine', () => { lineParts, [], 4, + 0, 10, -1, 'none', @@ -530,6 +538,7 @@ suite('viewLineRenderer.renderLine', () => { lineParts, [], 4, + 0, 10, -1, 'none', @@ -569,6 +578,7 @@ suite('viewLineRenderer.renderLine', () => { lineParts, [], 4, + 0, 10, -1, 'none', @@ -599,6 +609,7 @@ suite('viewLineRenderer.renderLine', () => { lineParts, [], 4, + 0, 10, -1, 'none', @@ -646,6 +657,7 @@ suite('viewLineRenderer.renderLine', () => { lineParts, [], 4, + 0, 10, -1, 'none', @@ -728,6 +740,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens(tokens), [], 4, + 0, 10, -1, renderWhitespace, @@ -754,6 +767,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(21, 3)]), [new LineDecoration(1, 22, 'link', InlineDecorationType.Regular)], 4, + 0, 10, -1, 'none', @@ -794,6 +808,7 @@ suite('viewLineRenderer.renderLine 2', () => { new LineDecoration(13, 51, 'detected-link', InlineDecorationType.Regular) ], 4, + 0, 10, -1, 'none', @@ -1209,6 +1224,7 @@ suite('viewLineRenderer.renderLine 2', () => { new LineDecoration(2, 8, 'c', InlineDecorationType.Regular), ], 4, + 0, 10, -1, 'none', @@ -1250,6 +1266,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(4, 3)]), [new LineDecoration(1, 2, 'before', InlineDecorationType.Before)], 4, + 0, 10, -1, 'all', @@ -1283,6 +1300,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(4, 3)]), [new LineDecoration(2, 3, 'before', InlineDecorationType.Before)], 4, + 0, 10, -1, 'all', @@ -1317,6 +1335,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(0, 3)]), [new LineDecoration(1, 2, 'before', InlineDecorationType.Before)], 4, + 0, 10, -1, 'all', @@ -1347,6 +1366,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(7, 3)]), [new LineDecoration(7, 8, 'inline-folded', InlineDecorationType.After)], 2, + 0, 10, 10000, 'none', @@ -1381,6 +1401,7 @@ suite('viewLineRenderer.renderLine 2', () => { new LineDecoration(0, 1, 'after', InlineDecorationType.After), ], 2, + 0, 10, 10000, 'none', @@ -1414,6 +1435,7 @@ suite('viewLineRenderer.renderLine 2', () => { new LineDecoration(3, 3, 'ced-TextEditorDecorationType2-5e9b9b3f-4 ced-TextEditorDecorationType2-4', InlineDecorationType.After), ], 4, + 0, 10, 10000, 'none', @@ -1445,6 +1467,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(15, 3)]), [], 4, + 0, 10, 10000, 'none', @@ -1475,6 +1498,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(15, 3)]), [], 4, + 0, 10, 10000, 'all', @@ -1511,6 +1535,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(53, 3)]), [], 4, + 0, 10, 10000, 'none', @@ -1541,6 +1566,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(100, 3)]), [], 4, + 0, 10, 10000, 'none', @@ -1573,6 +1599,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(105, 3)]), [], 4, + 0, 10, 10000, 'none', @@ -1604,6 +1631,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(59, 3)]), [], 4, + 0, 10, 10000, 'boundary', @@ -1633,6 +1661,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(194, 3)]), [], 4, + 0, 10, 10000, 'none', @@ -1666,6 +1695,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens([createPart(194, 3)]), [], 4, + 0, 10, 10000, 'none', @@ -1695,6 +1725,7 @@ suite('viewLineRenderer.renderLine 2', () => { createViewLineTokens(parts), [], tabSize, + 0, 10, -1, 'none', diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts index 88c4fe05bc5..a46124eccb1 100644 --- a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts +++ b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts @@ -114,7 +114,7 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { test('issue #35162: wrappingIndent not consistently working', () => { let factory = new CharacterHardWrappingLineMapperFactory('', '\t '); let mapper = assertLineMapping(factory, 4, 24, ' t h i s |i s |a l |o n |g l |i n |e', WrappingIndent.Indent); - assert.equal(mapper!.wrappedLinesIndent, ' \t'); + assert.equal(mapper!.wrappedTextIndentLength, ' '.length); }); test('issue #75494: surrogate pairs', () => { @@ -125,6 +125,6 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { test('CharacterHardWrappingLineMapper - WrappingIndent.DeepIndent', () => { let factory = new CharacterHardWrappingLineMapperFactory('', '\t '); let mapper = assertLineMapping(factory, 4, 26, ' W e A r e T e s t |i n g D e |e p I n d |e n t a t |i o n', WrappingIndent.DeepIndent); - assert.equal(mapper!.wrappedLinesIndent, ' \t\t'); + assert.equal(mapper!.wrappedTextIndentLength, ' '.length); }); }); diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index 52dc680d78a..ba4228ce9a6 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -23,7 +23,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; suite('Editor ViewModel - SplitLinesCollection', () => { test('SplitLine', () => { let model1 = createModel('My First LineMy Second LineAnd another one'); - let line1 = createSplitLine([13, 14, 15], ''); + let line1 = createSplitLine([13, 14, 15], [13, 13 + 14, 13 + 14 + 15], 0); assert.equal(line1.getViewLineCount(), 3); assert.equal(line1.getViewLineContent(model1, 1, 0), 'My First Line'); @@ -52,38 +52,38 @@ suite('Editor ViewModel - SplitLinesCollection', () => { } model1 = createModel('My First LineMy Second LineAnd another one'); - line1 = createSplitLine([13, 14, 15], '\t'); + line1 = createSplitLine([13, 14, 15], [13, 13 + 14, 13 + 14 + 15], 4); assert.equal(line1.getViewLineCount(), 3); assert.equal(line1.getViewLineContent(model1, 1, 0), 'My First Line'); - assert.equal(line1.getViewLineContent(model1, 1, 1), '\tMy Second Line'); - assert.equal(line1.getViewLineContent(model1, 1, 2), '\tAnd another one'); + assert.equal(line1.getViewLineContent(model1, 1, 1), ' My Second Line'); + assert.equal(line1.getViewLineContent(model1, 1, 2), ' And another one'); assert.equal(line1.getViewLineMaxColumn(model1, 1, 0), 14); - assert.equal(line1.getViewLineMaxColumn(model1, 1, 1), 16); - assert.equal(line1.getViewLineMaxColumn(model1, 1, 2), 17); - for (let col = 1; col <= 14; col++) { - assert.equal(line1.getModelColumnOfViewPosition(0, col), col, 'getInputColumnOfOutputPosition(0, ' + col + ')'); - } - for (let col = 1; col <= 1; col++) { - assert.equal(line1.getModelColumnOfViewPosition(1, 1), 13 + col, 'getInputColumnOfOutputPosition(1, ' + col + ')'); - } - for (let col = 2; col <= 16; col++) { - assert.equal(line1.getModelColumnOfViewPosition(1, col), 13 + col - 1, 'getInputColumnOfOutputPosition(1, ' + col + ')'); - } - for (let col = 1; col <= 1; col++) { - assert.equal(line1.getModelColumnOfViewPosition(2, col), 13 + 14 + col, 'getInputColumnOfOutputPosition(2, ' + col + ')'); - } - for (let col = 2; col <= 17; col++) { - assert.equal(line1.getModelColumnOfViewPosition(2, col), 13 + 14 + col - 1, 'getInputColumnOfOutputPosition(2, ' + col + ')'); + assert.equal(line1.getViewLineMaxColumn(model1, 1, 1), 19); + assert.equal(line1.getViewLineMaxColumn(model1, 1, 2), 20); + + let actualViewColumnMapping: number[][] = []; + for (let lineIndex = 0; lineIndex < line1.getViewLineCount(); lineIndex++) { + let actualLineViewColumnMapping: number[] = []; + for (let col = 1; col <= line1.getViewLineMaxColumn(model1, 1, lineIndex); col++) { + actualLineViewColumnMapping.push(line1.getModelColumnOfViewPosition(lineIndex, col)); + } + actualViewColumnMapping.push(actualLineViewColumnMapping); } + assert.deepEqual(actualViewColumnMapping, [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], + [14, 14, 14, 14, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28], + [28, 28, 28, 28, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43], + ]); + for (let col = 1; col <= 13; col++) { - assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(0, col), 'getOutputPositionOfInputPosition(' + col + ')'); + assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(0, col), '6.getOutputPositionOfInputPosition(' + col + ')'); } for (let col = 1 + 13; col <= 14 + 13; col++) { - assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(1, 1 + col - 13), 'getOutputPositionOfInputPosition(' + col + ')'); + assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(1, 4 + col - 13), '7.getOutputPositionOfInputPosition(' + col + ')'); } for (let col = 1 + 13 + 14; col <= 15 + 14 + 13; col++) { - assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(2, 1 + col - 13 - 14), 'getOutputPositionOfInputPosition(' + col + ')'); + assert.deepEqual(line1.getViewPositionOfModelPosition(0, col), pos(2, 4 + col - 13 - 14), '8.getOutputPositionOfInputPosition(' + col + ')'); } }); @@ -612,12 +612,12 @@ suite('SplitLinesCollection', () => { ] }, { - content: ' world");', - minColumn: 4, - maxColumn: 12, + content: ' world");', + minColumn: 13, + maxColumn: 21, tokens: [ - { endIndex: 9, value: 15 }, - { endIndex: 11, value: 16 }, + { endIndex: 18, value: 15 }, + { endIndex: 20, value: 16 }, ] }, { @@ -654,28 +654,28 @@ suite('SplitLinesCollection', () => { ] }, { - content: ' world, this is a ', - minColumn: 4, - maxColumn: 21, + content: ' world, this is a ', + minColumn: 13, + maxColumn: 30, tokens: [ - { endIndex: 20, value: 28 }, + { endIndex: 29, value: 28 }, ] }, { - content: ' somewhat longer ', - minColumn: 4, + content: ' somewhat longer ', + minColumn: 13, + maxColumn: 29, + tokens: [ + { endIndex: 28, value: 28 }, + ] + }, + { + content: ' line");', + minColumn: 13, maxColumn: 20, tokens: [ - { endIndex: 19, value: 28 }, - ] - }, - { - content: ' line");', - minColumn: 4, - maxColumn: 11, - tokens: [ - { endIndex: 8, value: 28 }, - { endIndex: 10, value: 29 }, + { endIndex: 17, value: 28 }, + { endIndex: 19, value: 29 }, ] }, { @@ -772,16 +772,16 @@ function pos(lineNumber: number, column: number): Position { return new Position(lineNumber, column); } -function createSplitLine(splitLengths: number[], wrappedLinesPrefix: string, isVisible: boolean = true): SplitLine { - return new SplitLine(createLineMapping(splitLengths, wrappedLinesPrefix), isVisible); +function createSplitLine(splitLengths: number[], breakingOffsetsVisibleColumn: number[], wrappedTextIndentWidth: number, isVisible: boolean = true): SplitLine { + return new SplitLine(createLineMapping(splitLengths, breakingOffsetsVisibleColumn, wrappedTextIndentWidth), isVisible); } -function createLineMapping(breakingLengths: number[], wrappedLinesPrefix: string): LineBreakingData { +function createLineMapping(breakingLengths: number[], breakingOffsetsVisibleColumn: number[], wrappedTextIndentWidth: number): LineBreakingData { let sums: number[] = []; for (let i = 0; i < breakingLengths.length; i++) { sums[i] = (i > 0 ? sums[i - 1] : 0) + breakingLengths[i]; } - return new LineBreakingData(sums, wrappedLinesPrefix); + return new LineBreakingData(sums, breakingOffsetsVisibleColumn, wrappedTextIndentWidth); } function createModel(text: string): ISimpleModel { From c476068dc7d19b803266588609ad473a881a075c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 8 Jan 2020 13:09:00 +0100 Subject: [PATCH 066/843] Use better editor API --- .../contrib/welcome/walkThrough/browser/walkThroughInput.ts | 3 ++- .../contrib/welcome/walkThrough/browser/walkThroughPart.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput.ts index 2b7a9668bd2..c8c3d2d6cb8 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput.ts @@ -11,6 +11,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import * as marked from 'vs/base/common/marked/marked'; import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; +import { EndOfLinePreference } from 'vs/editor/common/model'; export class WalkThroughModel extends EditorModel { @@ -111,7 +112,7 @@ export class WalkThroughInput extends EditorInput { return ''; }; - const markdown = ref.object.textEditorModel.getLinesContent().join('\n'); + const markdown = ref.object.textEditorModel.getValue(EndOfLinePreference.LF); marked(markdown, { renderer }); return Promise.all(snippets) diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index 149c916f53d..7b4b87ebad0 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -39,6 +39,7 @@ import { Dimension, size } from 'vs/base/browser/dom'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { domEvent } from 'vs/base/browser/event'; +import { EndOfLinePreference } from 'vs/editor/common/model'; export const WALK_THROUGH_FOCUS = new RawContextKey('interactivePlaygroundFocus', false); @@ -278,7 +279,7 @@ export class WalkThroughPart extends BaseEditor { return; } - const content = model.main.textEditorModel.getLinesContent().join('\n'); + const content = model.main.textEditorModel.getValue(EndOfLinePreference.LF); if (!strings.endsWith(input.getResource().path, '.md')) { this.content.innerHTML = content; this.updateSizeClasses(); From 2d4510bd8702f50981918c2fcb7816a0a227d773 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 8 Jan 2020 14:50:53 +0100 Subject: [PATCH 067/843] ability to check/uncheck changes --- .../bulkEdit/browser/bulkEdit.contribution.ts | 7 +- .../contrib/bulkEdit/browser/bulkEdit.css | 25 ++ .../contrib/bulkEdit/browser/bulkEditPanel.ts | 33 +-- .../bulkEdit/browser/bulkEditPreview.ts | 233 +++++++++++++----- .../contrib/bulkEdit/browser/bulkEditTree.ts | 132 +++++++--- 5 files changed, 315 insertions(+), 115 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index 3116faa32bf..fbd30d04361 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -31,8 +31,8 @@ class BulkEditPreviewContribution { return edit; } - const apply = await panel.setInput(edit); - if (!apply) { + const newEditOrUndefined = await panel.setInput(edit); + if (!newEditOrUndefined) { return { edits: [] }; } @@ -41,8 +41,7 @@ class BulkEditPreviewContribution { // this._panelService.hideActivePanel(); // } - // todo@joh get 'real' edit - return edit; + return newEditOrUndefined; } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css index 14043b9d484..e23be66097c 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css @@ -23,4 +23,29 @@ display: none; } +.monaco-workbench .bulk-edit-panel .monaco-tl-contents { + display: flex; +} +.monaco-workbench .bulk-edit-panel .monaco-tl-contents .edit-checkbox { + height: 16px; + width: 16px; + min-height: 16px; + min-width: 16px; + align-self: center; + margin-right: 4px; + border: 1px solid transparent; + border-radius: 2px; +} + +.monaco-workbench .bulk-edit-panel .monaco-tl-contents .edit-checkbox.disabled { + opacity: .5; +} + +.monaco-workbench .bulk-edit-panel .monaco-tl-contents .edit-checkbox.codicon[class*='codicon-'] { + font-size: 13px; +} + +.monaco-workbench .bulk-edit-panel .monaco-tl-contents .edit-checkbox.codicon:not(.checked)::before { + opacity: 0; +} diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts index 45593fb8558..11e71dfdfb4 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts @@ -41,7 +41,8 @@ export class BulkEditPanel extends Panel { private readonly _disposables = new DisposableStore(); private readonly _sessionDisposables = new DisposableStore(); - private _currentResolve?: (apply: boolean) => void; + private _currentResolve?: (edit?: WorkspaceEdit) => void; + private _currentInput?: BulkFileOperations; constructor( @IInstantiationService private readonly _instaService: IInstantiationService, @@ -74,10 +75,11 @@ export class BulkEditPanel extends Panel { this._tree = this._instaService.createInstance( WorkbenchAsyncDataTree, this.getId(), treeContainer, new BulkEditDelegate(), - [new TextEditElementRenderer(), this._instaService.createInstance(FileElementRenderer)], + [this._instaService.createInstance(TextEditElementRenderer), this._instaService.createInstance(FileElementRenderer)], this._instaService.createInstance(BulkEditDataSource), { - identityProvider: new BulkEditIdentityProvider() + identityProvider: new BulkEditIdentityProvider(), + expandOnlyOnTwistieClick: true } ); @@ -112,18 +114,20 @@ export class BulkEditPanel extends Panel { this.getContainer()!.dataset['state'] = state; } - async setInput(edit: WorkspaceEdit): Promise { + async setInput(edit: WorkspaceEdit): Promise { this._setState(State.Data); this._sessionDisposables.clear(); - if (this._currentResolve) { - this._currentResolve(false); + this._currentResolve(undefined); this._currentResolve = undefined; } const input = await this._instaService.invokeFunction(BulkFileOperations.create, edit); - this._sessionDisposables.add(this._instaService.createInstance(BulkEditPreviewProvider, input)); + const provider = this._instaService.createInstance(BulkEditPreviewProvider, input); + + this._sessionDisposables.add(provider); + this._currentInput = input; this._acceptAction.enabled = true; this._discardAction.enabled = true; @@ -146,32 +150,31 @@ export class BulkEditPanel extends Panel { this._setState(State.Message); this._sessionDisposables.clear(); if (this._currentResolve) { - this._currentResolve(accept); + this._currentResolve(accept ? this._currentInput?.asWorkspaceEdit() : undefined); this._acceptAction.enabled = false; this._discardAction.enabled = false; - this._tree.setInput(new BulkFileOperations([])); } } - private async _previewTextEditElement(edit: TextEditElement): Promise { + private async _previewTextEditElement(element: TextEditElement): Promise { let leftResource: URI; try { - (await this._textModelService.createModelReference(edit.parent.uri)).dispose(); - leftResource = edit.parent.uri; + (await this._textModelService.createModelReference(element.parent.uri)).dispose(); + leftResource = element.parent.uri; } catch { leftResource = BulkEditPreviewProvider.emptyPreview; } - const previewUri = BulkEditPreviewProvider.asPreviewUri(edit.parent.uri); + const previewUri = BulkEditPreviewProvider.asPreviewUri(element.parent.uri); this._editorService.openEditor({ leftResource, rightResource: previewUri, - label: localize('edt.title', "{0} (Refactor Preview)", this._labelService.getUriLabel(edit.parent.uri)), + label: localize('edt.title', "{0} (Refactor Preview)", this._labelService.getUriLabel(element.parent.uri)), options: { - selection: edit.edit.range + selection: element.edit.edit.range // preserveFocus, // pinned, // revealIfVisible: true diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index 654ad0d8361..4f93c0b9ad1 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -8,14 +8,44 @@ import { URI } from 'vs/base/common/uri'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; -import { WorkspaceEdit, isResourceTextEdit, TextEdit } from 'vs/editor/common/modes'; +import { WorkspaceEdit, isResourceTextEdit, TextEdit, ResourceTextEdit, ResourceFileEdit } from 'vs/editor/common/modes'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { mergeSort } from 'vs/base/common/arrays'; +import { mergeSort, coalesceInPlace } from 'vs/base/common/arrays'; import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { values } from 'vs/base/common/map'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IFileService } from 'vs/platform/files/common/files'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; + +class CheckedObject { + + private _checked: boolean = true; + + constructor(protected _emitter: Emitter) { } + + updateChecked(checked: boolean) { + if (this._checked !== checked) { + this._checked = checked; + this._emitter.fire(this); + } + } + + isChecked(): boolean { + return this._checked; + } +} + +export class BulkTextEdit extends CheckedObject { + + constructor( + readonly parent: BulkFileOperation, + readonly edit: TextEdit, + emitter: Emitter + ) { + super(emitter); + } +} export const enum BulkFileOperationType { None = 0, @@ -24,44 +54,62 @@ export const enum BulkFileOperationType { Rename = 0b0100, } -export class BulkFileOperation { +export class BulkFileOperation extends CheckedObject { type = BulkFileOperationType.None; - textEdits: TextEdit[] = []; + textEdits: BulkTextEdit[] = []; + originalEdits = new Map(); - constructor(readonly uri: URI) { } - - addType(type: BulkFileOperationType) { - this.type += type; + constructor( + readonly uri: URI, + readonly parent: BulkFileOperations + ) { + super(parent._onDidChangeCheckedState); } - addEdits(edits: TextEdit[]) { - this.textEdits = this.textEdits.concat(edits); + addEdit(index: number, type: BulkFileOperationType, edit: ResourceTextEdit | ResourceFileEdit, ) { + this.type += type; + this.originalEdits.set(index, edit); + if (isResourceTextEdit(edit)) { + this.textEdits = this.textEdits.concat(edit.edits.map(edit => new BulkTextEdit(this, edit, this._emitter))); + } } } export class BulkFileOperations { static async create(accessor: ServicesAccessor, bulkEdit: WorkspaceEdit): Promise { + const result = accessor.get(IInstantiationService).createInstance(BulkFileOperations, bulkEdit); + return await result._init(); + } + readonly _onDidChangeCheckedState = new Emitter(); + readonly onDidChangeCheckedState: Event = this._onDidChangeCheckedState.event; + + readonly fileOperations: BulkFileOperation[] = []; + + constructor( + private readonly _bulkEdit: WorkspaceEdit, + @IFileService private readonly _fileService: IFileService, + ) { } + + async _init() { const operationByResource = new Map(); - const fileService = accessor.get(IFileService); - - for (const edit of bulkEdit.edits) { + for (let idx = 0; idx < this._bulkEdit.edits.length; idx++) { + const edit = this._bulkEdit.edits[idx]; let uri: URI; let type: BulkFileOperationType; - let textEdits: TextEdit[] | undefined; if (isResourceTextEdit(edit)) { type = BulkFileOperationType.None; uri = edit.resource; - textEdits = edit.edits; + } else if (edit.newUri && edit.oldUri) { type = BulkFileOperationType.Rename; uri = edit.oldUri; - if (edit.options?.overwrite === undefined && edit.options?.ignoreIfExists && await fileService.exists(uri)) { + if (edit.options?.overwrite === undefined && edit.options?.ignoreIfExists && await this._fileService.exists(uri)) { // noop -> "soft" rename to something that already exists continue; } @@ -69,7 +117,7 @@ export class BulkFileOperations { } else if (edit.oldUri) { type = BulkFileOperationType.Delete; uri = edit.oldUri; - if (edit.options?.ignoreIfNotExists && !await fileService.exists(uri)) { + if (edit.options?.ignoreIfNotExists && !await this._fileService.exists(uri)) { // noop -> "soft" delete something that doesn't exist continue; } @@ -77,7 +125,7 @@ export class BulkFileOperations { } else if (edit.newUri) { type = BulkFileOperationType.Create; uri = edit.newUri; - if (edit.options?.overwrite === undefined && edit.options?.ignoreIfExists && await fileService.exists(uri)) { + if (edit.options?.overwrite === undefined && edit.options?.ignoreIfExists && await this._fileService.exists(uri)) { // noop -> "soft" create something that already exists continue; } @@ -90,20 +138,64 @@ export class BulkFileOperations { const key = uri.toString(); let operation = operationByResource.get(key); if (!operation) { - operation = new BulkFileOperation(uri); + operation = new BulkFileOperation(uri, this); operationByResource.set(key, operation); } - - operation.addType(type); - if (textEdits) { - operation.addEdits(textEdits); - } + operation.addEdit(idx, type, edit); } - return new BulkFileOperations(values(operationByResource)); + operationByResource.forEach(value => this.fileOperations.push(value)); + return this; } - constructor(readonly fileOperations: BulkFileOperation[]) { } + asWorkspaceEdit(): WorkspaceEdit { + const result: WorkspaceEdit = { edits: [] }; + let allAccepted = true; + for (let file of this.fileOperations) { + + if (!file.isChecked()) { + allAccepted = false; + continue; + } + + const keyOfEdit = (edit: TextEdit) => JSON.stringify(edit); + const checkedEdits = new Set(); + + for (let edit of file.textEdits) { + if (edit.isChecked()) { + checkedEdits.add(keyOfEdit(edit.edit)); + } + } + + file.originalEdits.forEach((value, idx) => { + + if (isResourceTextEdit(value)) { + let newValue: ResourceTextEdit = { ...value, edits: [] }; + let allEditsAccepted = true; + for (let edit of value.edits) { + if (!checkedEdits.has(keyOfEdit(edit))) { + allEditsAccepted = false; + } else { + newValue.edits.push(edit); + } + } + if (!allEditsAccepted) { + value = newValue; + allAccepted = false; + } + } + + result.edits[idx] = value; + }); + } + if (!allAccepted) { + // only return a new edit when something has changed + coalesceInPlace(result.edits); + return result; + } + return this._bulkEdit; + + } } export class BulkEditPreviewProvider implements ITextModelContentProvider { @@ -122,6 +214,7 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider { private readonly _disposables = new DisposableStore(); private readonly _ready: Promise; + private readonly _modelPreviewEdits = new Map(); constructor( private readonly _operations: BulkFileOperations, @@ -130,54 +223,70 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider { @ITextModelService private readonly _textModelResolverService: ITextModelService ) { this._disposables.add(this._textModelResolverService.registerTextModelContentProvider(BulkEditPreviewProvider.Schema, this)); - this._ready = this._prepareModels(); + this._ready = this._init(); } dispose(): void { this._disposables.dispose(); } - private async _prepareModels() { + private async _init() { + for (let operation of this._operations.fileOperations) { + await this._applyTextEditsToPreviewModel(operation); + } + this._disposables.add(this._operations.onDidChangeCheckedState(element => { + let operation = element instanceof BulkFileOperation ? element : element.parent; + this._applyTextEditsToPreviewModel(operation); + })); + } - const getOrCreatePreviewModel = async (uri: URI) => { - const previewUri = BulkEditPreviewProvider.asPreviewUri(uri); - let model = this._modelService.getModel(previewUri); - if (!model) { - try { - // try: copy existing - const ref = await this._textModelResolverService.createModelReference(uri); - const sourceModel = ref.object.textEditorModel; - model = this._modelService.createModel( - createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), - this._modeService.create(sourceModel.getLanguageIdentifier().language), - previewUri - ); - ref.dispose(); + private async _applyTextEditsToPreviewModel(operation: BulkFileOperation) { + const model = await this._getOrCreatePreviewModel(operation.uri); - } catch { - // create NEW model - model = this._modelService.createModel( - '', - this._modeService.createByFilepathOrFirstLine(previewUri), - previewUri - ); - } + // undo edits that have been done before + let undoEdits = this._modelPreviewEdits.get(model.id); + if (undoEdits) { + model.applyEdits(undoEdits); + } + // compute new edits + const newEdits = mergeSort( + operation.textEdits.filter(edit => edit.isChecked() && edit.parent.isChecked()).map(edit => EditOperation.replaceMove(Range.lift(edit.edit.range), edit.edit.text)), + (a, b) => Range.compareRangesUsingStarts(a.range, b.range) + ); + // apply edits and keep undo edits + undoEdits = model.applyEdits(newEdits); + this._modelPreviewEdits.set(model.id, undoEdits); + } + + private async _getOrCreatePreviewModel(uri: URI) { + const previewUri = BulkEditPreviewProvider.asPreviewUri(uri); + let model = this._modelService.getModel(previewUri); + if (!model) { + try { + // try: copy existing + const ref = await this._textModelResolverService.createModelReference(uri); + const sourceModel = ref.object.textEditorModel; + model = this._modelService.createModel( + createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), + this._modeService.create(sourceModel.getLanguageIdentifier().language), + previewUri + ); + ref.dispose(); + + } catch { + // create NEW model + model = this._modelService.createModel( + '', + this._modeService.createByFilepathOrFirstLine(previewUri), + previewUri + ); } // this is a little weird but otherwise editors and other cusomers // will dispose my models before they should be disposed... // And all of this is off the eventloop to prevent endless recursion new Promise(async () => this._disposables.add(await this._textModelResolverService.createModelReference(model!.uri))); - return model; - }; - - for (let operation of this._operations.fileOperations) { - const model = await getOrCreatePreviewModel(operation.uri); - const editOperations = mergeSort( - operation.textEdits.map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text)), - (a, b) => Range.compareRangesUsingStarts(a.range, b.range) - ); - model.applyEdits(editOperations); } + return model; } async provideTextContent(previewUri: URI) { diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 13fa844ffb5..b1c5e7b8219 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree'; -import * as modes from 'vs/editor/common/modes'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { IResourceLabel, ResourceLabels, DEFAULT_LABELS_CONTAINER } from 'vs/workbench/browser/labels'; @@ -15,11 +14,14 @@ import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list import { Range } from 'vs/editor/common/core/range'; import * as dom from 'vs/base/browser/dom'; import { ITextModel } from 'vs/editor/common/model'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TextModel } from 'vs/editor/common/model/textModel'; import { ILabelService } from 'vs/platform/label/common/label'; -import { BulkFileOperations, BulkFileOperation, BulkFileOperationType } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { BulkFileOperations, BulkFileOperation, BulkFileOperationType, BulkTextEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; import { localize } from 'vs/nls'; +import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachCheckboxStyler } from 'vs/platform/theme/common/styler'; // --- VIEW MODEL @@ -42,14 +44,13 @@ export class FileElement { this.uri = edit.uri; this.typeLabel = FileElement._typeLabels[edit.type] || ''; } - } export class TextEditElement { constructor( readonly parent: FileElement, - readonly edit: modes.TextEdit, + readonly edit: BulkTextEdit, readonly prefix: string, readonly selecting: string, readonly inserting: string, readonly suffix: string ) { } } @@ -94,7 +95,7 @@ export class BulkEditDataSource implements IAsyncDataSource { - const range = Range.lift(edit.range); + const range = Range.lift(edit.edit.range); const tokens = textModel.getLineTokens(range.endLineNumber); let suffixLen = 0; @@ -107,7 +108,7 @@ export class BulkEditDataSource implements IAsyncDataSource { + if (this._element) { + this._element.edit.updateChecked(_checkbox.checked); + } + })); + this._disposables.add(attachCheckboxStyler(_checkbox, themeService)); + } + + dispose(): void { + this._element = undefined; + this._disposables.dispose(); + this._checkbox.dispose(); + this._label.dispose(); + } + + set(element: FileElement, matches: FuzzyScore | undefined) { + this._element = element; + this._checkbox.checked = element.edit.isChecked(); + this._label.setResource({ + name: this._labelService.getUriLabel(element.uri, { relative: true }), + description: element.typeLabel, + resource: element.uri, + }, { + matches: createMatches(matches), + }); + } } export class FileElementRenderer implements ITreeRenderer { @@ -148,10 +184,9 @@ export class FileElementRenderer implements ITreeRenderer, _index: number, template: FileElementTemplate): void { - - template.label.setResource({ - name: this._labelService.getUriLabel(node.element.uri, { relative: true }), - description: node.element.typeLabel, - resource: node.element.uri, - }, { - matches: createMatches(node.filterData), - }); + template.set(node.element, node.filterData); } disposeTemplate(template: FileElementTemplate): void { - template.label.dispose(); + template.dispose(); } } class TextEditElementTemplate { - constructor(readonly label: HighlightedLabel) { } -} -export class TextEditElementRenderer implements ITreeRenderer { + private readonly _disposables = new DisposableStore(); + private readonly _localDisposables = new DisposableStore(); - static readonly id = 'TextEditElementRenderer'; + constructor( + private readonly _checkbox: Checkbox, + private readonly _label: HighlightedLabel, + @IThemeService themeService: IThemeService + ) { - readonly templateId: string = TextEditElementRenderer.id; - - renderTemplate(container: HTMLElement): TextEditElementTemplate { - const label = new HighlightedLabel(container, false); - dom.addClass(label.element, 'textedit'); - return new TextEditElementTemplate(label); + this._disposables.add(attachCheckboxStyler(_checkbox, themeService)); } - renderElement({ element }: ITreeNode, _index: number, template: TextEditElementTemplate): void { + dispose(): void { + this._localDisposables.dispose(); + this._disposables.dispose(); + this._checkbox.dispose(); + } + + set(element: TextEditElement) { + this._localDisposables.clear(); + this._localDisposables.add(this._checkbox.onChange(() => element.edit.updateChecked(this._checkbox.checked))); + this._localDisposables.add(element.edit.parent.parent.onDidChangeCheckedState(() => { + dom.toggleClass(this._checkbox.domNode, 'disabled', !element.edit.parent.isChecked()); + })); let value = ''; value += element.prefix; @@ -205,7 +246,30 @@ export class TextEditElementRenderer implements ITreeRenderer { + + static readonly id = 'TextEditElementRenderer'; + + readonly templateId: string = TextEditElementRenderer.id; + + constructor(@IInstantiationService private readonly _instaService: IInstantiationService) { } + + renderTemplate(container: HTMLElement): TextEditElementTemplate { + const checkbox = new Checkbox({ actionClassName: 'codicon-check edit-checkbox', isChecked: true, title: '' }); + container.appendChild(checkbox.domNode); + + const label = new HighlightedLabel(container, false); + dom.addClass(label.element, 'textedit'); + return this._instaService.createInstance(TextEditElementTemplate, checkbox, label); + } + + renderElement({ element }: ITreeNode, _index: number, template: TextEditElementTemplate): void { + template.set(element); } disposeTemplate(_template: TextEditElementTemplate): void { } From c4b499dc63beb64bf3e4564af4c1184f8a72a568 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 8 Jan 2020 14:57:46 +0100 Subject: [PATCH 068/843] Clarify members visibility --- .../pieceTreeTextBuffer/pieceTreeBase.ts | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts index 1c844dd66c4..984931ce7c7 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts @@ -728,7 +728,7 @@ export class PieceTreeBase { // #endregion // #region Piece Table - insert(offset: number, value: string, eolNormalized: boolean = false): void { + public insert(offset: number, value: string, eolNormalized: boolean = false): void { this._EOLNormalized = this._EOLNormalized && eolNormalized; this._lastVisitedLine.lineNumber = 0; this._lastVisitedLine.value = ''; @@ -826,7 +826,7 @@ export class PieceTreeBase { this.computeBufferMetadata(); } - delete(offset: number, cnt: number): void { + public delete(offset: number, cnt: number): void { this._lastVisitedLine.lineNumber = 0; this._lastVisitedLine.value = ''; @@ -899,7 +899,7 @@ export class PieceTreeBase { this.computeBufferMetadata(); } - insertContentToNodeLeft(value: string, node: TreeNode) { + private insertContentToNodeLeft(value: string, node: TreeNode) { // we are inserting content to the beginning of node let nodesToDel: TreeNode[] = []; if (this.shouldCheckCRLF() && this.endWithCR(value) && this.startWithLF(node)) { @@ -934,7 +934,7 @@ export class PieceTreeBase { this.deleteNodes(nodesToDel); } - insertContentToNodeRight(value: string, node: TreeNode) { + private insertContentToNodeRight(value: string, node: TreeNode) { // we are inserting to the right of this node. if (this.adjustCarriageReturnFromNext(value, node)) { // move \n to the new node. @@ -952,9 +952,9 @@ export class PieceTreeBase { this.validateCRLFWithPrevNode(newNode); } - positionInBuffer(node: TreeNode, remainder: number): BufferCursor; - positionInBuffer(node: TreeNode, remainder: number, ret: BufferCursor): null; - positionInBuffer(node: TreeNode, remainder: number, ret?: BufferCursor): BufferCursor | null { + private positionInBuffer(node: TreeNode, remainder: number): BufferCursor; + private positionInBuffer(node: TreeNode, remainder: number, ret: BufferCursor): null; + private positionInBuffer(node: TreeNode, remainder: number, ret?: BufferCursor): BufferCursor | null { let piece = node.piece; let bufferIndex = node.piece.bufferIndex; let lineStarts = this._buffers[bufferIndex].lineStarts; @@ -1002,7 +1002,7 @@ export class PieceTreeBase { }; } - getLineFeedCnt(bufferIndex: number, start: BufferCursor, end: BufferCursor): number { + private getLineFeedCnt(bufferIndex: number, start: BufferCursor, end: BufferCursor): number { // we don't need to worry about start: abc\r|\n, or abc|\r, or abc|\n, or abc|\r\n doesn't change the fact that, there is one line break after start. // now let's take care of end: abc\r|\n, if end is in between \r and \n, we need to add line feed count by 1 if (end.column === 0) { @@ -1032,18 +1032,18 @@ export class PieceTreeBase { } } - offsetInBuffer(bufferIndex: number, cursor: BufferCursor): number { + private offsetInBuffer(bufferIndex: number, cursor: BufferCursor): number { let lineStarts = this._buffers[bufferIndex].lineStarts; return lineStarts[cursor.line] + cursor.column; } - deleteNodes(nodes: TreeNode[]): void { + private deleteNodes(nodes: TreeNode[]): void { for (let i = 0; i < nodes.length; i++) { rbDelete(this, nodes[i]); } } - createNewPieces(text: string): Piece[] { + private createNewPieces(text: string): Piece[] { if (text.length > AverageBufferSize) { // the content is large, operations like substring, charCode becomes slow // so here we split it into smaller chunks, just like what we did for CR/LF normalization @@ -1128,11 +1128,11 @@ export class PieceTreeBase { return [newPiece]; } - getLinesRawContent(): string { + public getLinesRawContent(): string { return this.getContentOfSubTree(this.root); } - getLineRawContent(lineNumber: number, endOffset: number = 0): string { + public getLineRawContent(lineNumber: number, endOffset: number = 0): string { let x = this.root; let ret = ''; @@ -1204,7 +1204,7 @@ export class PieceTreeBase { return ret; } - computeBufferMetadata() { + private computeBufferMetadata() { let x = this.root; let lfCnt = 1; @@ -1222,7 +1222,7 @@ export class PieceTreeBase { } // #region node operations - getIndexOf(node: TreeNode, accumulatedValue: number): { index: number, remainder: number } { + private getIndexOf(node: TreeNode, accumulatedValue: number): { index: number, remainder: number } { let piece = node.piece; let pos = this.positionInBuffer(node, accumulatedValue); let lineCnt = pos.line - piece.start.line; @@ -1239,7 +1239,7 @@ export class PieceTreeBase { return { index: lineCnt, remainder: pos.column }; } - getAccumulatedValue(node: TreeNode, index: number) { + private getAccumulatedValue(node: TreeNode, index: number) { if (index < 0) { return 0; } @@ -1253,7 +1253,7 @@ export class PieceTreeBase { } } - deleteNodeTail(node: TreeNode, pos: BufferCursor) { + private deleteNodeTail(node: TreeNode, pos: BufferCursor) { const piece = node.piece; const originalLFCnt = piece.lineFeedCnt; const originalEndOffset = this.offsetInBuffer(piece.bufferIndex, piece.end); @@ -1277,7 +1277,7 @@ export class PieceTreeBase { updateTreeMetadata(this, node, size_delta, lf_delta); } - deleteNodeHead(node: TreeNode, pos: BufferCursor) { + private deleteNodeHead(node: TreeNode, pos: BufferCursor) { const piece = node.piece; const originalLFCnt = piece.lineFeedCnt; const originalStartOffset = this.offsetInBuffer(piece.bufferIndex, piece.start); @@ -1299,7 +1299,7 @@ export class PieceTreeBase { updateTreeMetadata(this, node, size_delta, lf_delta); } - shrinkNode(node: TreeNode, start: BufferCursor, end: BufferCursor) { + private shrinkNode(node: TreeNode, start: BufferCursor, end: BufferCursor) { const piece = node.piece; const originalStartPos = piece.start; const originalEndPos = piece.end; @@ -1334,7 +1334,7 @@ export class PieceTreeBase { this.validateCRLFWithPrevNode(newNode); } - appendToNode(node: TreeNode, value: string): void { + private appendToNode(node: TreeNode, value: string): void { if (this.adjustCarriageReturnFromNext(value, node)) { value += '\n'; } @@ -1374,7 +1374,7 @@ export class PieceTreeBase { updateTreeMetadata(this, node, value.length, lf_delta); } - nodeAt(offset: number): NodePosition { + private nodeAt(offset: number): NodePosition { let x = this.root; let cache = this._searchCache.get(offset); if (cache) { @@ -1409,7 +1409,7 @@ export class PieceTreeBase { return null!; } - nodeAt2(lineNumber: number, column: number): NodePosition { + private nodeAt2(lineNumber: number, column: number): NodePosition { let x = this.root; let nodeStartOffset = 0; @@ -1476,7 +1476,7 @@ export class PieceTreeBase { return null!; } - nodeCharCodeAt(node: TreeNode, offset: number): number { + private nodeCharCodeAt(node: TreeNode, offset: number): number { if (node.piece.lineFeedCnt < 1) { return -1; } @@ -1485,7 +1485,7 @@ export class PieceTreeBase { return buffer.buffer.charCodeAt(newOffset); } - offsetOfNode(node: TreeNode): number { + private offsetOfNode(node: TreeNode): number { if (!node) { return 0; } @@ -1504,11 +1504,11 @@ export class PieceTreeBase { // #endregion // #region CRLF - shouldCheckCRLF() { + private shouldCheckCRLF() { return !(this._EOLNormalized && this._EOL === '\n'); } - startWithLF(val: string | TreeNode): boolean { + private startWithLF(val: string | TreeNode): boolean { if (typeof val === 'string') { return val.charCodeAt(0) === 10; } @@ -1532,7 +1532,7 @@ export class PieceTreeBase { return this._buffers[piece.bufferIndex].buffer.charCodeAt(startOffset) === 10; } - endWithCR(val: string | TreeNode): boolean { + private endWithCR(val: string | TreeNode): boolean { if (typeof val === 'string') { return val.charCodeAt(val.length - 1) === 13; } @@ -1544,7 +1544,7 @@ export class PieceTreeBase { return this.nodeCharCodeAt(val, val.piece.length - 1) === 13; } - validateCRLFWithPrevNode(nextNode: TreeNode) { + private validateCRLFWithPrevNode(nextNode: TreeNode) { if (this.shouldCheckCRLF() && this.startWithLF(nextNode)) { let node = nextNode.prev(); if (this.endWithCR(node)) { @@ -1553,7 +1553,7 @@ export class PieceTreeBase { } } - validateCRLFWithNextNode(node: TreeNode) { + private validateCRLFWithNextNode(node: TreeNode) { if (this.shouldCheckCRLF() && this.endWithCR(node)) { let nextNode = node.next(); if (this.startWithLF(nextNode)) { @@ -1562,7 +1562,7 @@ export class PieceTreeBase { } } - fixCRLF(prev: TreeNode, next: TreeNode) { + private fixCRLF(prev: TreeNode, next: TreeNode) { let nodesToDel: TreeNode[] = []; // update node let lineStarts = this._buffers[prev.piece.bufferIndex].lineStarts; @@ -1617,7 +1617,7 @@ export class PieceTreeBase { } } - adjustCarriageReturnFromNext(value: string, node: TreeNode): boolean { + private adjustCarriageReturnFromNext(value: string, node: TreeNode): boolean { if (this.shouldCheckCRLF() && this.endWithCR(value)) { let nextNode = node.next(); if (this.startWithLF(nextNode)) { @@ -1667,7 +1667,7 @@ export class PieceTreeBase { return callback(node) && this.iterate(node.right, callback); } - getNodeContent(node: TreeNode) { + private getNodeContent(node: TreeNode) { if (node === SENTINEL) { return ''; } @@ -1695,7 +1695,7 @@ export class PieceTreeBase { * / * z */ - rbInsertRight(node: TreeNode | null, p: Piece): TreeNode { + private rbInsertRight(node: TreeNode | null, p: Piece): TreeNode { let z = new TreeNode(p, NodeColor.Red); z.left = SENTINEL; z.right = SENTINEL; @@ -1727,7 +1727,7 @@ export class PieceTreeBase { * \ * z */ - rbInsertLeft(node: TreeNode | null, p: Piece): TreeNode { + private rbInsertLeft(node: TreeNode | null, p: Piece): TreeNode { let z = new TreeNode(p, NodeColor.Red); z.left = SENTINEL; z.right = SENTINEL; @@ -1751,7 +1751,7 @@ export class PieceTreeBase { return z; } - getContentOfSubTree(node: TreeNode): string { + private getContentOfSubTree(node: TreeNode): string { let str = ''; this.iterate(node, node => { From fae5b730f89be9d89fc5765f802cd23e73dbe1bc Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 8 Jan 2020 15:04:29 +0100 Subject: [PATCH 069/843] Make PieceTreeBase.getLinesContent a lot faster --- .../pieceTreeTextBuffer/pieceTreeBase.ts | 88 ++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts index 984931ce7c7..ac23e98889c 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts @@ -511,7 +511,93 @@ export class PieceTreeBase { } public getLinesContent(): string[] { - return this.getContentOfSubTree(this.root).split(/\r\n|\r|\n/); + let lines: string[] = []; + let linesLength = 0; + let currentLine = ''; + let danglingCR = false; + + this.iterate(this.root, node => { + if (node === SENTINEL) { + return true; + } + + const piece = node.piece; + let pieceLength = piece.length; + if (pieceLength === 0) { + return true; + } + + const buffer = this._buffers[piece.bufferIndex].buffer; + const lineStarts = this._buffers[piece.bufferIndex].lineStarts; + + const pieceStartLine = piece.start.line; + const pieceEndLine = piece.end.line; + let pieceStartOffset = lineStarts[pieceStartLine] + piece.start.column; + + if (danglingCR) { + if (buffer.charCodeAt(pieceStartOffset) === CharCode.LineFeed) { + // pretend the \n was in the previous piece.. + pieceStartOffset++; + pieceLength--; + } + lines[linesLength++] = currentLine; + currentLine = ''; + danglingCR = false; + if (pieceLength === 0) { + return true; + } + } + + if (pieceStartLine === pieceEndLine) { + // this piece has no new lines + if (!this._EOLNormalized && buffer.charCodeAt(pieceStartOffset + pieceLength - 1) === CharCode.CarriageReturn) { + danglingCR = true; + currentLine += buffer.substr(pieceStartOffset, pieceLength - 1); + } else { + currentLine += buffer.substr(pieceStartOffset, pieceLength); + } + return true; + } + + // add the text before the first line start in this piece + currentLine += ( + this._EOLNormalized + ? buffer.substring(pieceStartOffset, Math.max(pieceStartOffset, lineStarts[pieceStartLine + 1] - this._EOLLength)) + : buffer.substring(pieceStartOffset, lineStarts[pieceStartLine + 1]).replace(/(\r\n|\r|\n)$/, '') + ); + lines[linesLength++] = currentLine; + + for (let line = pieceStartLine + 1; line < pieceEndLine; line++) { + currentLine = ( + this._EOLNormalized + ? buffer.substring(lineStarts[line], lineStarts[line + 1] - this._EOLLength) + : buffer.substring(lineStarts[line], lineStarts[line + 1]).replace(/(\r\n|\r|\n)$/, '') + ); + lines[linesLength++] = currentLine; + } + + if (!this._EOLNormalized && buffer.charCodeAt(lineStarts[pieceEndLine] + piece.end.column - 1) === CharCode.CarriageReturn) { + danglingCR = true; + if (piece.end.column === 0) { + // The last line ended with a \r, let's undo the push, it will be pushed by next iteration + linesLength--; + } else { + currentLine = buffer.substr(lineStarts[pieceEndLine], piece.end.column - 1); + } + } else { + currentLine = buffer.substr(lineStarts[pieceEndLine], piece.end.column); + } + + return true; + }); + + if (danglingCR) { + lines[linesLength++] = currentLine; + currentLine = ''; + } + + lines[linesLength++] = currentLine; + return lines; } public getLength(): number { From ed4173796d08cdbbddcbcd0fa3bca74ee41dee41 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 8 Jan 2020 17:08:48 +0100 Subject: [PATCH 070/843] ts sem --- .../src/features/semanticColoring.ts | 210 ++++++++++++++++++ .../src/languageProvider.ts | 1 + 2 files changed, 211 insertions(+) create mode 100644 extensions/typescript-language-features/src/features/semanticColoring.ts diff --git a/extensions/typescript-language-features/src/features/semanticColoring.ts b/extensions/typescript-language-features/src/features/semanticColoring.ts new file mode 100644 index 00000000000..10a92c50bd2 --- /dev/null +++ b/extensions/typescript-language-features/src/features/semanticColoring.ts @@ -0,0 +1,210 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ITypeScriptServiceClient, ExecConfig, ServerResponse } from '../typescriptService'; +import * as Proto from '../protocol'; + +enum TokenType { + 'class', + 'enum', + 'interface', + 'namespace', + 'typeParameter', + 'type', + 'parameter', + 'variable', + 'property', + 'constant', + 'function', + 'member', + _sentinel +} + + +enum TokenModifier { + 'declaration', + 'static', + 'async', + _sentinel +} + +class SemanticTokensProvider implements vscode.SemanticTokensProvider { + + constructor( + private readonly client: ITypeScriptServiceClient + ) { + } + + getLegend(): vscode.SemanticTokensLegend { + const tokenTypes = []; + for (let i = 0; i < TokenType._sentinel; i++) { + tokenTypes.push(TokenType[i]); + } + const tokenModifiers = []; + for (let i = 0; i < TokenModifier._sentinel; i++) { + tokenModifiers.push(TokenModifier[i]); + } + return new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers); + } + + async provideSemanticTokens(document: vscode.TextDocument, _options: vscode.SemanticTokensRequestOptions, token: vscode.CancellationToken): Promise { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return null; + } + + const args: ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs = { + file: file, + start: 0, + length: document.getText().length, + }; + + const versionBeforeRequest = document.version; + + const response = await (this.client as ExperimentalProtocol.IExtendedTypeScriptServiceClient).execute('encodedSemanticClassifications-full', args, token); + const versionAfterRequest = document.version; + + if (versionBeforeRequest !== versionAfterRequest) { + // A new request will come in soon... + return null; + } + + if (response.type !== 'response') { + return null; + } + if (!response.body) { + return null; + } + + const builder = new vscode.SemanticTokensBuilder(); + + const tsTokens = response.body.spans; + for (let i = 0, len = Math.floor(tsTokens.length / 3); i < len; i++) { + + const tokenType = tokenTypeMap[tsTokens[3 * i + 2]]; + if (typeof tokenType === 'number') { + console.log(TokenType[tokenType]); + const offset = tsTokens[3 * i]; + const length = tsTokens[3 * i + 1]; + + // we can use the document's range conversion methods because + // the result is at the same version as the document + const startPos = document.positionAt(offset); + const endPos = document.positionAt(offset + length); + + for (let line = startPos.line; line <= endPos.line; line++) { + const startCharacter = (line === startPos.line ? startPos.character : 0); + const endCharacter = (line === endPos.line ? endPos.character : document.lineAt(line).text.length); + builder.push(line, startCharacter, endCharacter - startCharacter, tokenType, 0); + } + } + } + + return new vscode.SemanticTokens(builder.build()); + } + +} + +export function register( + selector: vscode.DocumentSelector, + client: ITypeScriptServiceClient +) { + const provider = new SemanticTokensProvider(client); + return vscode.languages.registerSemanticTokensProvider(selector, provider, provider.getLegend()); +} + +const tokenTypeMap: number[] = []; +tokenTypeMap[ExperimentalProtocol.ClassificationType.className] = TokenType.class; +tokenTypeMap[ExperimentalProtocol.ClassificationType.enumName] = TokenType.enum; +tokenTypeMap[ExperimentalProtocol.ClassificationType.interfaceName] = TokenType.interface; +tokenTypeMap[ExperimentalProtocol.ClassificationType.moduleName] = TokenType.namespace; +tokenTypeMap[ExperimentalProtocol.ClassificationType.typeParameterName] = TokenType.typeParameter; +tokenTypeMap[ExperimentalProtocol.ClassificationType.typeAliasName] = TokenType.type; +tokenTypeMap[ExperimentalProtocol.ClassificationType.parameterName] = TokenType.parameter; + +export namespace ExperimentalProtocol { + + export interface IExtendedTypeScriptServiceClient { + execute( + command: K, + args: ExperimentalProtocol.ExtendedTsServerRequests[K][0], + token: vscode.CancellationToken, + config?: ExecConfig + ): Promise>; + } + + /** + * A request to get encoded semantic classifications for a span in the file + */ + export interface EncodedSemanticClassificationsRequest extends Proto.FileRequest { + arguments: EncodedSemanticClassificationsRequestArgs; + } + + /** + * Arguments for EncodedSemanticClassificationsRequest request. + */ + export interface EncodedSemanticClassificationsRequestArgs extends Proto.FileRequestArgs { + /** + * Start position of the span. + */ + start: number; + /** + * Length of the span. + */ + length: number; + } + + export const enum EndOfLineState { + None, + InMultiLineCommentTrivia, + InSingleQuoteStringLiteral, + InDoubleQuoteStringLiteral, + InTemplateHeadOrNoSubstitutionTemplate, + InTemplateMiddleOrTail, + InTemplateSubstitutionPosition, + } + + export const enum ClassificationType { + comment = 1, + identifier = 2, + keyword = 3, + numericLiteral = 4, + operator = 5, + stringLiteral = 6, + regularExpressionLiteral = 7, + whiteSpace = 8, + text = 9, + punctuation = 10, + className = 11, + enumName = 12, + interfaceName = 13, + moduleName = 14, + typeParameterName = 15, + typeAliasName = 16, + parameterName = 17, + docCommentTagName = 18, + jsxOpenTagName = 19, + jsxCloseTagName = 20, + jsxSelfClosingTagName = 21, + jsxAttribute = 22, + jsxText = 23, + jsxAttributeStringLiteralValue = 24, + bigintLiteral = 25, + } + + export interface EncodedSemanticClassificationsResponse extends Proto.Response { + body?: { + endOfLineState: EndOfLineState; + spans: number[]; + }; + } + + export interface ExtendedTsServerRequests { + 'encodedSemanticClassifications-full': [ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs, ExperimentalProtocol.EncodedSemanticClassificationsResponse]; + } +} + + diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 0d434f24ff6..9d06ecdeddc 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -78,6 +78,7 @@ export default class LanguageProvider extends Disposable { import('./features/signatureHelp').then(provider => this._register(provider.register(selector, this.client))), import('./features/tagClosing').then(provider => this._register(provider.register(selector, this.description.id, this.client))), import('./features/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), + import('./features/semanticColoring').then(provider => this._register(provider.register(selector, this.client))), ]); } From 9971b4a9c6728287e9fafd63cb8b6e98a2841749 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 8 Jan 2020 18:15:20 +0100 Subject: [PATCH 071/843] ViewPane instead of Panel --- src/vs/base/browser/ui/splitview/paneview.ts | 4 +- .../bulkEdit/browser/bulkEdit.contribution.ts | 43 +++++++++++++------ .../{bulkEditPanel.ts => bulkEditPane.ts} | 40 +++++++++-------- 3 files changed, 53 insertions(+), 34 deletions(-) rename src/vs/workbench/contrib/bulkEdit/browser/{bulkEditPanel.ts => bulkEditPane.ts} (82%) diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts index 037ffdce27b..e50add0846a 100644 --- a/src/vs/base/browser/ui/splitview/paneview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -44,8 +44,8 @@ export abstract class Pane extends Disposable implements IView { private static readonly HEADER_SIZE = 22; readonly element: HTMLElement; - private header!: HTMLElement; - private body!: HTMLElement; + protected header!: HTMLElement; + protected body!: HTMLElement; protected _expanded: boolean; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index fbd30d04361..1882e742a44 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -9,9 +9,11 @@ import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } fr import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; -import { BulkEditPanel } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPanel'; -import { Extensions as PanelExtensions, PanelDescriptor, PanelRegistry } from 'vs/workbench/browser/panel'; +import { BulkEditPanel as BulkEditPane } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPane'; +import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewsRegistry } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; +import { ViewPaneContainer, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { PaneCompositePanel } from 'vs/workbench/browser/panel'; class BulkEditPreviewContribution { @@ -19,19 +21,22 @@ class BulkEditPreviewContribution { @IPanelService private _panelService: IPanelService, @IBulkEditService bulkEditService: IBulkEditService, ) { - bulkEditService.setPreviewHandler(edit => this._previewEdit(edit)); } private async _previewEdit(edit: WorkspaceEdit) { - const panel = this._panelService.openPanel(BulkEditPanel.ID, true); - if (!(panel instanceof BulkEditPanel)) { - // error? + let view: ViewPane | undefined; + const activePanel = this._panelService.openPanel(BulkEditPane.ID, true); + if (activePanel instanceof PaneCompositePanel) { + view = activePanel.getViewPaneContainer().getView(BulkEditPane.ID); + } + + if (!(view instanceof BulkEditPane)) { return edit; } - const newEditOrUndefined = await panel.setInput(edit); + const newEditOrUndefined = await view.setInput(edit); if (!newEditOrUndefined) { return { edits: [] }; } @@ -49,10 +54,20 @@ Registry.as(WorkbenchExtensions.Workbench).regi BulkEditPreviewContribution, LifecyclePhase.Ready ); -Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create( - BulkEditPanel, - BulkEditPanel.ID, - localize('panel', "Refactor Preview"), - 'bulkEditPanel', - 10 -)); + +const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ + id: BulkEditPane.ID, + name: localize('panel', "Refactor Preview"), + ctorDescriptor: { + ctor: ViewPaneContainer, + arguments: [BulkEditPane.ID, '', { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }] + } +}, ViewContainerLocation.Panel); + +Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ + id: BulkEditPane.ID, + name: localize('panel', "Refactor Preview"), + canToggleVisibility: false, + ctorDescriptor: { ctor: BulkEditPane }, +}], container); + diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts similarity index 82% rename from src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts rename to src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 11e71dfdfb4..610130d47f1 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPanel.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -4,16 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./bulkEdit'; -import { Panel } from 'vs/workbench/browser/panel'; -import { Dimension } from 'vs/base/browser/dom'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; -import { IStorageService } from 'vs/platform/storage/common/storage'; +import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { Action } from 'vs/base/common/actions'; import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; @@ -23,13 +19,18 @@ import { BulkEditPreviewProvider, BulkFileOperations } from 'vs/workbench/contri import { ILabelService } from 'vs/platform/label/common/label'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { URI } from 'vs/base/common/uri'; +import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; const enum State { Data = 'data', Message = 'message' } -export class BulkEditPanel extends Panel { +export class BulkEditPanel extends ViewPane { static readonly ID = 'BulkEditPanel'; @@ -49,11 +50,15 @@ export class BulkEditPanel extends Panel { @IEditorService private readonly _editorService: IEditorService, @ILabelService private readonly _labelService: ILabelService, @ITextModelService private readonly _textModelService: ITextModelService, - @ITelemetryService telemetryService: ITelemetryService, - @IThemeService themeService: IThemeService, - @IStorageService storageService: IStorageService, + @IKeybindingService keybindingService: IKeybindingService, + @IContextMenuService contextMenuService: IContextMenuService, + @IConfigurationService configurationService: IConfigurationService, + @IContextKeyService contextKeyService: IContextKeyService ) { - super(BulkEditPanel.ID, telemetryService, themeService, storageService); + super( + { id: BulkEditPanel.ID, title: localize('title', "Refactor Preview") }, + keybindingService, contextMenuService, configurationService, contextKeyService + ); } dispose(): void { @@ -61,8 +66,7 @@ export class BulkEditPanel extends Panel { this._disposables.dispose(); } - create(parent: HTMLElement): void { - super.create(parent); + protected renderBody(parent: HTMLElement): void { parent.className = 'bulk-edit-panel'; // tree @@ -73,7 +77,7 @@ export class BulkEditPanel extends Panel { parent.appendChild(treeContainer); this._tree = this._instaService.createInstance( - WorkbenchAsyncDataTree, this.getId(), treeContainer, + WorkbenchAsyncDataTree, this.id, treeContainer, new BulkEditDelegate(), [this._instaService.createInstance(TextEditElementRenderer), this._instaService.createInstance(FileElementRenderer)], this._instaService.createInstance(BulkEditDataSource), @@ -88,7 +92,7 @@ export class BulkEditPanel extends Panel { if (first instanceof TextEditElement) { this._previewTextEditElement(first); } else if (first instanceof FileElement) { - this._previewFileElement(first); + this._previewFileElement(); } })); @@ -106,12 +110,12 @@ export class BulkEditPanel extends Panel { return [this._acceptAction, this._discardAction]; } - layout(dimension: Dimension): void { - this._tree.layout(dimension.height, dimension.width); + protected layoutBody(height: number, width: number): void { + this._tree.layout(height, width); } private _setState(state: State): void { - this.getContainer()!.dataset['state'] = state; + this.body.dataset['state'] = state; } async setInput(edit: WorkspaceEdit): Promise { @@ -182,7 +186,7 @@ export class BulkEditPanel extends Panel { }); } - private _previewFileElement(edit: FileElement): void { + private _previewFileElement(): void { } } From 8fcec963744ae426c2358005c428f00038233c44 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 8 Jan 2020 18:34:38 +0100 Subject: [PATCH 072/843] use standard file rendering, file operation type as css-class (need UI) --- .../contrib/bulkEdit/browser/bulkEditPane.ts | 2 +- .../contrib/bulkEdit/browser/bulkEditTree.ts | 30 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 610130d47f1..1982c15c8d5 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -67,7 +67,7 @@ export class BulkEditPanel extends ViewPane { } protected renderBody(parent: HTMLElement): void { - parent.className = 'bulk-edit-panel'; + parent.classList.add('bulk-edit-panel', 'show-file-icons'); // tree const treeContainer = document.createElement('div'); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index b1c5e7b8219..c7d86541996 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -16,12 +16,12 @@ import * as dom from 'vs/base/browser/dom'; import { ITextModel } from 'vs/editor/common/model'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { ILabelService } from 'vs/platform/label/common/label'; import { BulkFileOperations, BulkFileOperation, BulkFileOperationType, BulkTextEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; import { localize } from 'vs/nls'; import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachCheckboxStyler } from 'vs/platform/theme/common/styler'; +import { FileKind } from 'vs/platform/files/common/files'; // --- VIEW MODEL @@ -144,8 +144,7 @@ class FileElementTemplate { constructor( private readonly _checkbox: Checkbox, private readonly _label: IResourceLabel, - @IThemeService themeService: IThemeService, - @ILabelService private readonly _labelService: ILabelService, + @IThemeService themeService: IThemeService ) { this._disposables.add(_checkbox.onChange(() => { if (this._element) { @@ -162,15 +161,26 @@ class FileElementTemplate { this._label.dispose(); } - set(element: FileElement, matches: FuzzyScore | undefined) { + set(element: FileElement, score: FuzzyScore | undefined) { this._element = element; this._checkbox.checked = element.edit.isChecked(); - this._label.setResource({ - name: this._labelService.getUriLabel(element.uri, { relative: true }), - description: element.typeLabel, - resource: element.uri, - }, { - matches: createMatches(matches), + + const extraClasses: string[] = []; + if (element.edit.type & BulkFileOperationType.Create) { + extraClasses.push('create'); + } + if (element.edit.type & BulkFileOperationType.Delete) { + extraClasses.push('delete'); + } + if (element.edit.type & BulkFileOperationType.Rename) { + extraClasses.push('rename'); + } + this._label.setFile(element.uri, { + matches: createMatches(score), + fileKind: FileKind.FILE, + fileDecorations: { colors: true, badges: false }, + // parentCount: element.edit.textEdits.length || undefined, + extraClasses, }); } } From d6a3ca72b5a4ff505353006d39f38b7228c7b36e Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Mon, 6 Jan 2020 22:06:51 -0500 Subject: [PATCH 073/843] Setting to control whether to focus the inline editor in peek widget by default. Fixes #23001 --- src/vs/editor/common/config/editorOptions.ts | 10 ++++++++++ .../contrib/gotoSymbol/peek/referencesController.ts | 10 +++++++++- src/vs/monaco.d.ts | 5 +++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 3adac8cf46b..4daf79c9167 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -542,6 +542,11 @@ export interface IEditorOptions { * Controls fading out of unused variables. */ showUnused?: boolean; + /** + * Controls whether to focus the inline editor in the peek widget by default. + * Defaults to false. + */ + peekWidgetFocusInlineEditor?: boolean; } export interface IEditorConstructionOptions extends IEditorOptions { @@ -3121,6 +3126,7 @@ export const enum EditorOption { overviewRulerBorder, overviewRulerLanes, parameterHints, + peekWidgetFocusInlineEditor, quickSuggestions, quickSuggestionsDelay, readOnly, @@ -3485,6 +3491,10 @@ export const EditorOptions = { 3, 0, 3 )), parameterHints: register(new EditorParameterHints()), + peekWidgetFocusInlineEditor: register(new EditorBooleanOption( + EditorOption.peekWidgetFocusInlineEditor, 'peekWidgetFocusInlineEditor', false, + { description: nls.localize('peekWidgetFocusInlineEditor', "Controls whether to focus the inline editor in the peek widget by default.") } + )), quickSuggestions: register(new EditorQuickSuggestions()), quickSuggestionsDelay: register(new EditorIntOption( EditorOption.quickSuggestionsDelay, 'quickSuggestionsDelay', diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts index a140ff4501a..8bf6c0966da 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts @@ -25,6 +25,7 @@ import { IListService, WorkbenchListFocusContextKey } from 'vs/platform/list/bro import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; export const ctxReferenceSearchVisible = new RawContextKey('referenceSearchVisible', false); @@ -163,7 +164,11 @@ export abstract class ReferencesController implements editorCommon.IEditorContri let pos = new Position(range.startLineNumber, range.startColumn); let selection = this._model.nearestReference(uri, pos); if (selection) { - return this._widget.setSelection(selection); + return this._widget.setSelection(selection).then(() => { + if (this._widget && this._editor.getOption(EditorOption.peekWidgetFocusInlineEditor)) { + this._widget.focusOnPreviewEditor(); + } + }); } } return undefined; @@ -201,10 +206,13 @@ export abstract class ReferencesController implements editorCommon.IEditorContri } const target = this._model.nextOrPreviousReference(source, fwd); const editorFocus = this._editor.hasTextFocus(); + const previewEditorFocus = this._widget.isPreviewEditorFocused(); await this._widget.setSelection(target); await this._gotoReference(target); if (editorFocus) { this._editor.focus(); + } else if (this._widget && previewEditorFocus) { + this._widget.focusOnPreviewEditor(); } } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 32a3f131d6b..956c79ffad2 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2987,6 +2987,11 @@ declare namespace monaco.editor { * Controls fading out of unused variables. */ showUnused?: boolean; + /** + * Controls whether to focus the inline editor in the peek widget by default. + * Defaults to false. + */ + peekWidgetFocusInlineEditor?: boolean; } export interface IEditorConstructionOptions extends IEditorOptions { From f3a313a360f4959249aff59533120975a693c2c8 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 9 Jan 2020 10:15:01 +0100 Subject: [PATCH 074/843] Extract breaking conditions --- .../characterHardWrappingLineMapper.ts | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index e1fff8b193e..013e7c25bd9 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -126,13 +126,26 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor let breakOffset = 0; let breakOffsetVisibleColumn = 0; const len = lineText.length; + const len1 = len - 1; let breakingColumn = firstLineBreakingColumn; + let prevCharCode = CharCode.Null; + let prevCharCodeClass = CharacterClass.NONE; + let charCode = CharCode.Null; + let charCodeClass = CharacterClass.NONE; + let nextCharCode = (len > 0 ? lineText.charCodeAt(0) : CharCode.Null); + let nextCharCodeClass = classifier.get(nextCharCode); + for (let i = 0; i < len; i++) { // At this point, there is a certainty that the character before `i` fits on the current line, // but the character at `i` might not fit - const charCode = lineText.charCodeAt(i); + prevCharCode = charCode; + prevCharCodeClass = charCodeClass; + charCode = nextCharCode; + charCodeClass = nextCharCodeClass; + nextCharCode = (i < len1 ? lineText.charCodeAt(i + 1) : CharCode.Null); + nextCharCodeClass = classifier.get(nextCharCode); if (strings.isLowSurrogate(charCode)) { // A surrogate pair must always be considered as a single unit, so it is never to be broken @@ -140,16 +153,7 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor continue; } - const charCodeClass = classifier.get(charCode); - - if ( - (charCodeClass === CharacterClass.BREAK_BEFORE) - || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i > 0 && classifier.get(lineText.charCodeAt(i - 1)) !== CharacterClass.BREAK_BEFORE) - ) { - // This is a character that indicates that a break should happen before it - // (or) CJK breaking : before break : Kinsoku Shori : Don't break after a leading character, like an open bracket - // Since we are certain the character before `i` fits, there's no extra checking needed, - // just mark it as a nice breaking opportunity + if (prevCharCode !== CharCode.Null && canBreakBefore(charCodeClass, prevCharCodeClass)) { breakOffset = i; breakOffsetVisibleColumn = visibleColumn; } @@ -180,12 +184,7 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor } // At this point, there is a certainty that the character at `i` fits on the current line - if ( - (charCodeClass === CharacterClass.BREAK_AFTER && (hardWrappingIndent === WrappingIndent.None || i >= firstNonWhitespaceIndex)) - || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i + 1 < len && classifier.get(lineText.charCodeAt(i + 1)) !== CharacterClass.BREAK_AFTER) - ) { - // This is a character that indicates that a break should happen after it - // (or) CJK breaking : after break : Kinsoku Shori : Don't break before a trailing character, like a period + if (nextCharCode !== CharCode.Null && canBreakAfter(charCodeClass, nextCharCodeClass)) { breakOffset = i + 1; breakOffsetVisibleColumn = visibleColumn; } @@ -206,3 +205,21 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor function tabCharacterWidth(visibleColumn: number, tabSize: number): number { return (tabSize - (visibleColumn % tabSize)); } + +function canBreakBefore(charCodeClass: CharacterClass, prevCharCodeClass: CharacterClass): boolean { + // This is a character that indicates that a break should happen before it + // (or) CJK breaking : before break : Kinsoku Shori : Don't break after a leading character, like an open bracket + return ( + (charCodeClass === CharacterClass.BREAK_BEFORE) + || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && prevCharCodeClass !== CharacterClass.BREAK_BEFORE) + ); +} + +function canBreakAfter(charCodeClass: CharacterClass, nextCharCodeClass: CharacterClass): boolean { + // This is a character that indicates that a break should happen after it + // (or) CJK breaking : after break : Kinsoku Shori : Don't break before a trailing character, like a period + return ( + (charCodeClass === CharacterClass.BREAK_AFTER) + || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && nextCharCodeClass !== CharacterClass.BREAK_AFTER) + ); +} From 3d2581a5385355988f13b69f10a2bf4bbce1308a Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 9 Jan 2020 10:36:11 +0100 Subject: [PATCH 075/843] Pass in previousBreakingData to line mapping computation --- .../characterHardWrappingLineMapper.ts | 62 +++++++------ .../common/viewModel/splitLinesCollection.ts | 86 ++++++++++++------- .../editor/common/viewModel/viewModelImpl.ts | 4 +- .../characterHardWrappingLineMapper.test.ts | 2 +- 4 files changed, 91 insertions(+), 63 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 013e7c25bd9..8d58bcaeccf 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -72,50 +72,28 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor columnsForFullWidthChar = +columnsForFullWidthChar; //@perf let requests: string[] = []; + let previousBreakingData: (LineBreakingData | null)[] = []; return { - addRequest: (lineText: string) => { + addRequest: (lineText: string, previousLineBreakingData: LineBreakingData | null) => { requests.push(lineText); + previousBreakingData.push(previousLineBreakingData); }, finalize: () => { let result: (LineBreakingData | null)[] = []; for (let i = 0, len = requests.length; i < len; i++) { - result[i] = this._createLineMapping(requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + result[i] = this._createLineMapping(requests[i], previousBreakingData[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); } return result; } }; } - private _createLineMapping(lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { + private _createLineMapping(lineText: string, previousBreakingData: LineBreakingData | null, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { if (firstLineBreakingColumn === -1) { return null; } - let firstNonWhitespaceIndex = -1; - let wrappedTextIndentLength = 0; - if (hardWrappingIndent !== WrappingIndent.None) { - firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineText); - if (firstNonWhitespaceIndex !== -1) { - // Track existing indent - - for (let i = 0; i < firstNonWhitespaceIndex; i++) { - const charWidth = (lineText.charCodeAt(i) === CharCode.Tab ? tabCharacterWidth(wrappedTextIndentLength, tabSize) : 1); - wrappedTextIndentLength += charWidth; - } - - // Increase indent of continuation lines, if desired - const numberOfAdditionalTabs = (hardWrappingIndent === WrappingIndent.DeepIndent ? 2 : hardWrappingIndent === WrappingIndent.Indent ? 1 : 0); - for (let i = 0; i < numberOfAdditionalTabs; i++) { - const charWidth = tabCharacterWidth(wrappedTextIndentLength, tabSize); - wrappedTextIndentLength += charWidth; - } - - // Force sticking to beginning of line if no character would fit except for the indentation - if (wrappedTextIndentLength + columnsForFullWidthChar > firstLineBreakingColumn) { - wrappedTextIndentLength = 0; - } - } - } + const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); const wrappedLineBreakingColumn = firstLineBreakingColumn - wrappedTextIndentLength; const classifier = this.classifier; @@ -223,3 +201,31 @@ function canBreakAfter(charCodeClass: CharacterClass, nextCharCodeClass: Charact || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && nextCharCodeClass !== CharacterClass.BREAK_AFTER) ); } + +function computeWrappedTextIndentLength(lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): number { + let wrappedTextIndentLength = 0; + if (hardWrappingIndent !== WrappingIndent.None) { + const firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineText); + if (firstNonWhitespaceIndex !== -1) { + // Track existing indent + + for (let i = 0; i < firstNonWhitespaceIndex; i++) { + const charWidth = (lineText.charCodeAt(i) === CharCode.Tab ? tabCharacterWidth(wrappedTextIndentLength, tabSize) : 1); + wrappedTextIndentLength += charWidth; + } + + // Increase indent of continuation lines, if desired + const numberOfAdditionalTabs = (hardWrappingIndent === WrappingIndent.DeepIndent ? 2 : hardWrappingIndent === WrappingIndent.Indent ? 1 : 0); + for (let i = 0; i < numberOfAdditionalTabs; i++) { + const charWidth = tabCharacterWidth(wrappedTextIndentLength, tabSize); + wrappedTextIndentLength += charWidth; + } + + // Force sticking to beginning of line if no character would fit except for the indentation + if (wrappedTextIndentLength + columnsForFullWidthChar > firstLineBreakingColumn) { + wrappedTextIndentLength = 0; + } + } + } + return wrappedTextIndentLength; +} diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 113059661f9..7f6dcddd4ce 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -67,7 +67,10 @@ export class LineBreakingData { } export interface ILineMappingComputer { - addRequest(lineText: string): void; + /** + * Pass in previousLineBreakingData if the only difference is in breaking columns!!! + */ + addRequest(lineText: string, previousLineBreakingData: LineBreakingData | null): void; finalize(): (LineBreakingData | null)[]; } @@ -88,6 +91,7 @@ export interface ISplitLine { isVisible(): boolean; setVisible(isVisible: boolean): ISplitLine; + getLineBreakingData(): LineBreakingData | null; getViewLineCount(): number; getViewLineContent(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): string; getViewLineLength(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): number; @@ -286,7 +290,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { this.wrappingIndent = wrappingIndent; this.linePositionMapperFactory = linePositionMapperFactory; - this._constructLines(true); + this._constructLines(/*resetHiddenAreas*/true, null); } public dispose(): void { @@ -297,7 +301,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return new CoordinatesConverter(this); } - private _constructLines(resetHiddenAreas: boolean): void { + private _constructLines(resetHiddenAreas: boolean, previousLineMapping: ((LineBreakingData | null)[]) | null): void { this.lines = []; if (resetHiddenAreas) { @@ -308,7 +312,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { const lineCount = linesContent.length; const lineMappingComputer = this.linePositionMapperFactory.createLineMappingComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent); for (let i = 0; i < lineCount; i++) { - lineMappingComputer.addRequest(linesContent[i]); + lineMappingComputer.addRequest(linesContent[i], previousLineMapping ? previousLineMapping[i] : null); } const lineMappings = lineMappingComputer.finalize(); @@ -461,7 +465,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } this.tabSize = newTabSize; - this._constructLines(false); + this._constructLines(/*resetHiddenAreas*/false, null); return true; } @@ -471,11 +475,21 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return false; } + const onlyWrappingColumnChanged = (this.wrappingIndent === wrappingIndent && this.wrappingColumn !== wrappingColumn && this.columnsForFullWidthChar === columnsForFullWidthChar); + this.wrappingIndent = wrappingIndent; this.wrappingColumn = wrappingColumn; this.columnsForFullWidthChar = columnsForFullWidthChar; - this._constructLines(false); + let previousLineMapping: ((LineBreakingData | null)[]) | null = null; + if (onlyWrappingColumnChanged) { + previousLineMapping = []; + for (let i = 0, len = this.lines.length; i < len; i++) { + previousLineMapping[i] = this.lines[i].getLineBreakingData(); + } + } + + this._constructLines(/*resetHiddenAreas*/false, previousLineMapping); return true; } @@ -485,7 +499,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public onModelFlushed(): void { - this._constructLines(true); + this._constructLines(/*resetHiddenAreas*/true, null); } public onModelLinesDeleted(versionId: number, fromLineNumber: number, toLineNumber: number): viewEvents.ViewLinesDeletedEvent | null { @@ -1005,6 +1019,10 @@ class VisibleIdentitySplitLine implements ISplitLine { return InvisibleIdentitySplitLine.INSTANCE; } + public getLineBreakingData(): LineBreakingData | null { + return null; + } + public getViewLineCount(): number { return 1; } @@ -1076,6 +1094,10 @@ class InvisibleIdentitySplitLine implements ISplitLine { return VisibleIdentitySplitLine.INSTANCE; } + public getLineBreakingData(): LineBreakingData | null { + return null; + } + public getViewLineCount(): number { return 0; } @@ -1119,15 +1141,11 @@ class InvisibleIdentitySplitLine implements ISplitLine { export class SplitLine implements ISplitLine { - private readonly _breakOffsets: number[]; - private readonly _breakingOffsetsVisibleColumn: number[]; - private readonly _wrappedTextIndentLength: number; + private readonly _lineBreakingData: LineBreakingData; private _isVisible: boolean; - constructor(lineBreaking: LineBreakingData, isVisible: boolean) { - this._breakOffsets = lineBreaking.breakOffsets; - this._breakingOffsetsVisibleColumn = lineBreaking.breakingOffsetsVisibleColumn; - this._wrappedTextIndentLength = lineBreaking.wrappedTextIndentLength; + constructor(lineBreakingData: LineBreakingData, isVisible: boolean) { + this._lineBreakingData = lineBreakingData; this._isVisible = isVisible; } @@ -1140,22 +1158,26 @@ export class SplitLine implements ISplitLine { return this; } + public getLineBreakingData(): LineBreakingData | null { + return this._lineBreakingData; + } + public getViewLineCount(): number { if (!this._isVisible) { return 0; } - return this._breakOffsets.length; + return this._lineBreakingData.breakOffsets.length; } private getInputStartOffsetOfOutputLineIndex(outputLineIndex: number): number { - return LineBreakingData.getInputOffsetOfOutputPosition(this._breakOffsets, outputLineIndex, 0); + return LineBreakingData.getInputOffsetOfOutputPosition(this._lineBreakingData.breakOffsets, outputLineIndex, 0); } private getInputEndOffsetOfOutputLineIndex(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): number { - if (outputLineIndex + 1 === this._breakOffsets.length) { + if (outputLineIndex + 1 === this._lineBreakingData.breakOffsets.length) { return model.getLineMaxColumn(modelLineNumber) - 1; } - return LineBreakingData.getInputOffsetOfOutputPosition(this._breakOffsets, outputLineIndex + 1, 0); + return LineBreakingData.getInputOffsetOfOutputPosition(this._lineBreakingData.breakOffsets, outputLineIndex + 1, 0); } public getViewLineContent(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): string { @@ -1172,7 +1194,7 @@ export class SplitLine implements ISplitLine { }); if (outputLineIndex > 0) { - r = spaces(this._wrappedTextIndentLength) + r; + r = spaces(this._lineBreakingData.wrappedTextIndentLength) + r; } return r; @@ -1187,7 +1209,7 @@ export class SplitLine implements ISplitLine { let r = endOffset - startOffset; if (outputLineIndex > 0) { - r = this._wrappedTextIndentLength + r; + r = this._lineBreakingData.wrappedTextIndentLength + r; } return r; @@ -1198,7 +1220,7 @@ export class SplitLine implements ISplitLine { throw new Error('Not supported'); } if (outputLineIndex > 0) { - return this._wrappedTextIndentLength + 1; + return this._lineBreakingData.wrappedTextIndentLength + 1; } return 1; } @@ -1226,21 +1248,21 @@ export class SplitLine implements ISplitLine { }); if (outputLineIndex > 0) { - lineContent = spaces(this._wrappedTextIndentLength) + lineContent; + lineContent = spaces(this._lineBreakingData.wrappedTextIndentLength) + lineContent; } - let minColumn = (outputLineIndex > 0 ? this._wrappedTextIndentLength + 1 : 1); + let minColumn = (outputLineIndex > 0 ? this._lineBreakingData.wrappedTextIndentLength + 1 : 1); let maxColumn = lineContent.length + 1; let continuesWithWrappedLine = (outputLineIndex + 1 < this.getViewLineCount()); let deltaStartIndex = 0; if (outputLineIndex > 0) { - deltaStartIndex = this._wrappedTextIndentLength; + deltaStartIndex = this._lineBreakingData.wrappedTextIndentLength; } let lineTokens = model.getLineTokens(modelLineNumber); - const startVisibleColumn = (outputLineIndex === 0 ? 0 : this._breakingOffsetsVisibleColumn[outputLineIndex - 1]); + const startVisibleColumn = (outputLineIndex === 0 ? 0 : this._lineBreakingData.breakingOffsetsVisibleColumn[outputLineIndex - 1]); return new ViewLineData( lineContent, @@ -1273,25 +1295,25 @@ export class SplitLine implements ISplitLine { } let adjustedColumn = outputColumn - 1; if (outputLineIndex > 0) { - if (adjustedColumn < this._wrappedTextIndentLength) { + if (adjustedColumn < this._lineBreakingData.wrappedTextIndentLength) { adjustedColumn = 0; } else { - adjustedColumn -= this._wrappedTextIndentLength; + adjustedColumn -= this._lineBreakingData.wrappedTextIndentLength; } } - return LineBreakingData.getInputOffsetOfOutputPosition(this._breakOffsets, outputLineIndex, adjustedColumn) + 1; + return LineBreakingData.getInputOffsetOfOutputPosition(this._lineBreakingData.breakOffsets, outputLineIndex, adjustedColumn) + 1; } public getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position { if (!this._isVisible) { throw new Error('Not supported'); } - let r = LineBreakingData.getOutputPositionOfInputOffset(this._breakOffsets, inputColumn - 1); + let r = LineBreakingData.getOutputPositionOfInputOffset(this._lineBreakingData.breakOffsets, inputColumn - 1); let outputLineIndex = r.outputLineIndex; let outputColumn = r.outputOffset + 1; if (outputLineIndex > 0) { - outputColumn += this._wrappedTextIndentLength; + outputColumn += this._lineBreakingData.wrappedTextIndentLength; } // console.log('in -> out ' + deltaLineNumber + ',' + inputColumn + ' ===> ' + (deltaLineNumber+outputLineIndex) + ',' + outputColumn); @@ -1302,7 +1324,7 @@ export class SplitLine implements ISplitLine { if (!this._isVisible) { throw new Error('Not supported'); } - const r = LineBreakingData.getOutputPositionOfInputOffset(this._breakOffsets, inputColumn - 1); + const r = LineBreakingData.getOutputPositionOfInputOffset(this._lineBreakingData.breakOffsets, inputColumn - 1); return (deltaLineNumber + r.outputLineIndex); } } @@ -1421,7 +1443,7 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { public createLineMappingComputer(): ILineMappingComputer { let result: null[] = []; return { - addRequest: (lineText: string) => { + addRequest: (lineText: string, previousLineBreakingData: LineBreakingData | null) => { result.push(null); }, finalize: () => { diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index f437ceeab26..55100bc6c23 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -202,12 +202,12 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel switch (change.changeType) { case textModelEvents.RawContentChangedType.LinesInserted: { for (const line of change.detail) { - lineMappingComputer.addRequest(line); + lineMappingComputer.addRequest(line, null); } break; } case textModelEvents.RawContentChangedType.LineChanged: { - lineMappingComputer.addRequest(change.detail); + lineMappingComputer.addRequest(change.detail, null); break; } } diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts index a46124eccb1..a1aec876698 100644 --- a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts +++ b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts @@ -22,7 +22,7 @@ function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAf } const lineMappingComputer = factory.createLineMappingComputer(tabSize, breakAfter, 2, wrappingIndent); - lineMappingComputer.addRequest(rawText); + lineMappingComputer.addRequest(rawText, null); const lineMappings = lineMappingComputer.finalize(); const mapper = lineMappings[0]; From 4cb0c653fbd4ea698c7ee6a433d12ffbd369f2ee Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 9 Jan 2020 10:38:34 +0100 Subject: [PATCH 076/843] :lipstick: --- .../characterHardWrappingLineMapper.ts | 179 +++++++++--------- 1 file changed, 89 insertions(+), 90 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 8d58bcaeccf..61f5d8f02eb 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -81,103 +81,102 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor finalize: () => { let result: (LineBreakingData | null)[] = []; for (let i = 0, len = requests.length; i < len; i++) { - result[i] = this._createLineMapping(requests[i], previousBreakingData[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + result[i] = createLineMapping(this.classifier, previousBreakingData[i], requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); } return result; } }; } +} - private _createLineMapping(lineText: string, previousBreakingData: LineBreakingData | null, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { - if (firstLineBreakingColumn === -1) { - return null; - } - - const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); - const wrappedLineBreakingColumn = firstLineBreakingColumn - wrappedTextIndentLength; - - const classifier = this.classifier; - let breakingOffsets: number[] = []; - let breakingOffsetsVisibleColumn: number[] = []; - let breakingOffsetsCount: number = 0; - let visibleColumn = 0; - let breakOffset = 0; - let breakOffsetVisibleColumn = 0; - const len = lineText.length; - const len1 = len - 1; - - let breakingColumn = firstLineBreakingColumn; - let prevCharCode = CharCode.Null; - let prevCharCodeClass = CharacterClass.NONE; - let charCode = CharCode.Null; - let charCodeClass = CharacterClass.NONE; - let nextCharCode = (len > 0 ? lineText.charCodeAt(0) : CharCode.Null); - let nextCharCodeClass = classifier.get(nextCharCode); - - for (let i = 0; i < len; i++) { - // At this point, there is a certainty that the character before `i` fits on the current line, - // but the character at `i` might not fit - - prevCharCode = charCode; - prevCharCodeClass = charCodeClass; - charCode = nextCharCode; - charCodeClass = nextCharCodeClass; - nextCharCode = (i < len1 ? lineText.charCodeAt(i + 1) : CharCode.Null); - nextCharCodeClass = classifier.get(nextCharCode); - - if (strings.isLowSurrogate(charCode)) { - // A surrogate pair must always be considered as a single unit, so it is never to be broken - visibleColumn += 1; - continue; - } - - if (prevCharCode !== CharCode.Null && canBreakBefore(charCodeClass, prevCharCodeClass)) { - breakOffset = i; - breakOffsetVisibleColumn = visibleColumn; - } - - const charColumnSize = ( - charCode === CharCode.Tab - ? tabCharacterWidth(visibleColumn, tabSize) - : (strings.isFullWidthCharacter(charCode) ? columnsForFullWidthChar : 1) - ); - - visibleColumn += charColumnSize; - - // check if adding character at `i` will go over the breaking column - if (visibleColumn > breakingColumn && i !== 0) { - // We need to break at least before character at `i`: - - if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { - // Cannot break at `breakOffset`, must break at `i` - breakOffset = i; - breakOffsetVisibleColumn = visibleColumn - charColumnSize; - } - - breakingOffsets[breakingOffsetsCount] = breakOffset; - breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; - breakingOffsetsCount++; - breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; - breakOffset = 0; - } - - // At this point, there is a certainty that the character at `i` fits on the current line - if (nextCharCode !== CharCode.Null && canBreakAfter(charCodeClass, nextCharCodeClass)) { - breakOffset = i + 1; - breakOffsetVisibleColumn = visibleColumn; - } - } - - if (breakingOffsetsCount === 0) { - return null; - } - - // Add last segment - breakingOffsets[breakingOffsetsCount] = len; - breakingOffsetsVisibleColumn[breakingOffsetsCount] = visibleColumn; - - return new LineBreakingData(breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); +function createLineMapping(classifier: WrappingCharacterClassifier, previousBreakingData: LineBreakingData | null, lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { + if (firstLineBreakingColumn === -1) { + return null; } + + const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); + const wrappedLineBreakingColumn = firstLineBreakingColumn - wrappedTextIndentLength; + + let breakingOffsets: number[] = []; + let breakingOffsetsVisibleColumn: number[] = []; + let breakingOffsetsCount: number = 0; + let visibleColumn = 0; + let breakOffset = 0; + let breakOffsetVisibleColumn = 0; + const len = lineText.length; + const len1 = len - 1; + + let breakingColumn = firstLineBreakingColumn; + let prevCharCode = CharCode.Null; + let prevCharCodeClass = CharacterClass.NONE; + let charCode = CharCode.Null; + let charCodeClass = CharacterClass.NONE; + let nextCharCode = (len > 0 ? lineText.charCodeAt(0) : CharCode.Null); + let nextCharCodeClass = classifier.get(nextCharCode); + + for (let i = 0; i < len; i++) { + // At this point, there is a certainty that the character before `i` fits on the current line, + // but the character at `i` might not fit + + prevCharCode = charCode; + prevCharCodeClass = charCodeClass; + charCode = nextCharCode; + charCodeClass = nextCharCodeClass; + nextCharCode = (i < len1 ? lineText.charCodeAt(i + 1) : CharCode.Null); + nextCharCodeClass = classifier.get(nextCharCode); + + if (strings.isLowSurrogate(charCode)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + visibleColumn += 1; + continue; + } + + if (prevCharCode !== CharCode.Null && canBreakBefore(charCodeClass, prevCharCodeClass)) { + breakOffset = i; + breakOffsetVisibleColumn = visibleColumn; + } + + const charColumnSize = ( + charCode === CharCode.Tab + ? tabCharacterWidth(visibleColumn, tabSize) + : (strings.isFullWidthCharacter(charCode) ? columnsForFullWidthChar : 1) + ); + + visibleColumn += charColumnSize; + + // check if adding character at `i` will go over the breaking column + if (visibleColumn > breakingColumn && i !== 0) { + // We need to break at least before character at `i`: + + if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { + // Cannot break at `breakOffset`, must break at `i` + breakOffset = i; + breakOffsetVisibleColumn = visibleColumn - charColumnSize; + } + + breakingOffsets[breakingOffsetsCount] = breakOffset; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; + breakingOffsetsCount++; + breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; + breakOffset = 0; + } + + // At this point, there is a certainty that the character at `i` fits on the current line + if (nextCharCode !== CharCode.Null && canBreakAfter(charCodeClass, nextCharCodeClass)) { + breakOffset = i + 1; + breakOffsetVisibleColumn = visibleColumn; + } + } + + if (breakingOffsetsCount === 0) { + return null; + } + + // Add last segment + breakingOffsets[breakingOffsetsCount] = len; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = visibleColumn; + + return new LineBreakingData(breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); } function tabCharacterWidth(visibleColumn: number, tabSize: number): number { From 25e65e6e64618435934f54b8c24102f899ce69e7 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 9 Jan 2020 11:21:52 +0100 Subject: [PATCH 077/843] Simplify wrapping computation --- .../characterHardWrappingLineMapper.ts | 89 +++++++++---------- .../common/viewModel/splitLinesCollection.ts | 1 + .../viewModel/splitLinesCollection.test.ts | 2 +- 3 files changed, 42 insertions(+), 50 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 61f5d8f02eb..d1d9a51306d 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -94,64 +94,53 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea return null; } + const len = lineText.length; + if (len <= 1) { + return null; + } + const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); const wrappedLineBreakingColumn = firstLineBreakingColumn - wrappedTextIndentLength; let breakingOffsets: number[] = []; let breakingOffsetsVisibleColumn: number[] = []; let breakingOffsetsCount: number = 0; - let visibleColumn = 0; let breakOffset = 0; let breakOffsetVisibleColumn = 0; - const len = lineText.length; - const len1 = len - 1; let breakingColumn = firstLineBreakingColumn; - let prevCharCode = CharCode.Null; - let prevCharCodeClass = CharacterClass.NONE; - let charCode = CharCode.Null; - let charCodeClass = CharacterClass.NONE; - let nextCharCode = (len > 0 ? lineText.charCodeAt(0) : CharCode.Null); - let nextCharCodeClass = classifier.get(nextCharCode); + let prevCharCode = lineText.charCodeAt(0); + let prevCharCodeClass = classifier.get(prevCharCode); + let visibleColumn = computeCharWidth(prevCharCode, 0, tabSize, columnsForFullWidthChar); - for (let i = 0; i < len; i++) { - // At this point, there is a certainty that the character before `i` fits on the current line, - // but the character at `i` might not fit + for (let i = 1; i < len; i++) { + const charCode = lineText.charCodeAt(i); + const charCodeClass = classifier.get(charCode); - prevCharCode = charCode; - prevCharCodeClass = charCodeClass; - charCode = nextCharCode; - charCodeClass = nextCharCodeClass; - nextCharCode = (i < len1 ? lineText.charCodeAt(i + 1) : CharCode.Null); - nextCharCodeClass = classifier.get(nextCharCode); - - if (strings.isLowSurrogate(charCode)) { + if (strings.isHighSurrogate(prevCharCode)) { // A surrogate pair must always be considered as a single unit, so it is never to be broken visibleColumn += 1; + prevCharCode = charCode; + prevCharCodeClass = charCodeClass; continue; } - if (prevCharCode !== CharCode.Null && canBreakBefore(charCodeClass, prevCharCodeClass)) { + if (canBreak(prevCharCodeClass, charCodeClass)) { breakOffset = i; breakOffsetVisibleColumn = visibleColumn; } - const charColumnSize = ( - charCode === CharCode.Tab - ? tabCharacterWidth(visibleColumn, tabSize) - : (strings.isFullWidthCharacter(charCode) ? columnsForFullWidthChar : 1) - ); - - visibleColumn += charColumnSize; + const charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); + visibleColumn += charWidth; // check if adding character at `i` will go over the breaking column - if (visibleColumn > breakingColumn && i !== 0) { + if (visibleColumn > breakingColumn) { // We need to break at least before character at `i`: if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { // Cannot break at `breakOffset`, must break at `i` breakOffset = i; - breakOffsetVisibleColumn = visibleColumn - charColumnSize; + breakOffsetVisibleColumn = visibleColumn - charWidth; } breakingOffsets[breakingOffsetsCount] = breakOffset; @@ -161,11 +150,8 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea breakOffset = 0; } - // At this point, there is a certainty that the character at `i` fits on the current line - if (nextCharCode !== CharCode.Null && canBreakAfter(charCodeClass, nextCharCodeClass)) { - breakOffset = i + 1; - breakOffsetVisibleColumn = visibleColumn; - } + prevCharCode = charCode; + prevCharCodeClass = charCodeClass; } if (breakingOffsetsCount === 0) { @@ -176,31 +162,36 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea breakingOffsets[breakingOffsetsCount] = len; breakingOffsetsVisibleColumn[breakingOffsetsCount] = visibleColumn; - return new LineBreakingData(breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); + return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); +} + +function computeCharWidth(charCode: number, visibleColumn: number, tabSize: number, columnsForFullWidthChar: number): number { + if (charCode === CharCode.Tab) { + return (tabSize - (visibleColumn % tabSize)); + } + if (strings.isFullWidthCharacter(charCode)) { + return columnsForFullWidthChar; + } + return 1; } function tabCharacterWidth(visibleColumn: number, tabSize: number): number { return (tabSize - (visibleColumn % tabSize)); } -function canBreakBefore(charCodeClass: CharacterClass, prevCharCodeClass: CharacterClass): boolean { - // This is a character that indicates that a break should happen before it - // (or) CJK breaking : before break : Kinsoku Shori : Don't break after a leading character, like an open bracket +/** + * Kinsoku Shori : Don't break after a leading character, like an open bracket + * Kinsoku Shori : Don't break before a trailing character, like a period + */ +function canBreak(prevCharCodeClass: CharacterClass, charCodeClass: CharacterClass): boolean { return ( - (charCodeClass === CharacterClass.BREAK_BEFORE) + (prevCharCodeClass === CharacterClass.BREAK_AFTER) + || (prevCharCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && charCodeClass !== CharacterClass.BREAK_AFTER) + || (charCodeClass === CharacterClass.BREAK_BEFORE) || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && prevCharCodeClass !== CharacterClass.BREAK_BEFORE) ); } -function canBreakAfter(charCodeClass: CharacterClass, nextCharCodeClass: CharacterClass): boolean { - // This is a character that indicates that a break should happen after it - // (or) CJK breaking : after break : Kinsoku Shori : Don't break before a trailing character, like a period - return ( - (charCodeClass === CharacterClass.BREAK_AFTER) - || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && nextCharCodeClass !== CharacterClass.BREAK_AFTER) - ); -} - function computeWrappedTextIndentLength(lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): number { let wrappedTextIndentLength = 0; if (hardWrappingIndent !== WrappingIndent.None) { diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 7f6dcddd4ce..6346d5bbc4c 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -28,6 +28,7 @@ export class OutputPosition { export class LineBreakingData { constructor( + public readonly breakingColumn: number, public readonly breakOffsets: number[], public readonly breakingOffsetsVisibleColumn: number[], public readonly wrappedTextIndentLength: number diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index ba4228ce9a6..56f06a2a2bc 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -781,7 +781,7 @@ function createLineMapping(breakingLengths: number[], breakingOffsetsVisibleColu for (let i = 0; i < breakingLengths.length; i++) { sums[i] = (i > 0 ? sums[i - 1] : 0) + breakingLengths[i]; } - return new LineBreakingData(sums, breakingOffsetsVisibleColumn, wrappedTextIndentWidth); + return new LineBreakingData(0, sums, breakingOffsetsVisibleColumn, wrappedTextIndentWidth); } function createModel(text: string): ISimpleModel { From b8b0b558146b0d22a1190f63aaaabf9dd87c73ba Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 9 Jan 2020 13:58:44 +0100 Subject: [PATCH 078/843] wip --- .../characterHardWrappingLineMapper.ts | 272 ++++++++++++++++++ .../common/viewModel/splitLinesCollection.ts | 23 ++ .../characterHardWrappingLineMapper.test.ts | 95 ++++-- 3 files changed, 372 insertions(+), 18 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index d1d9a51306d..0790c9a6444 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -105,6 +105,222 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea let breakingOffsets: number[] = []; let breakingOffsetsVisibleColumn: number[] = []; let breakingOffsetsCount: number = 0; + + if (previousBreakingData/* && firstLineBreakingColumn >= 10 && Math.abs(previousBreakingData.breakingColumn - firstLineBreakingColumn) <= 3 */) { + const prevBreakingOffsets = previousBreakingData.breakOffsets; + const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakingOffsetsVisibleColumn; + + let breakingColumn = firstLineBreakingColumn; + const prevLen = prevBreakingOffsets.length; + let prevIndex = 0; + while (prevIndex < prevLen) { + + // Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break) + let breakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex]; + let breakOffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex]; + + if (breakOffsetVisibleColumn === breakingColumn) { + // perfect fit, nothing to do + breakingOffsets[breakingOffsetsCount] = breakOffset; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; + breakingOffsetsCount++; + breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; + prevIndex++; + } else if (breakOffsetVisibleColumn < breakingColumn) { + // try to add more characters + const initialBreakOffset = breakOffset; + let visibleColumn = breakOffsetVisibleColumn; + breakOffset = 0; + + let prevCharCode = lineText.charCodeAt(initialBreakOffset - 1); + let prevCharCodeClass = classifier.get(prevCharCode); + let mustBreak = false; + for (let i = initialBreakOffset; i < len; i++) { + const charCode = lineText.charCodeAt(i); + const charCodeClass = classifier.get(charCode); + + if (strings.isHighSurrogate(prevCharCode)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + visibleColumn += 1; + prevCharCode = charCode; + prevCharCodeClass = charCodeClass; + continue; + } + + if (canBreak(prevCharCodeClass, charCodeClass)) { + breakOffset = i; + breakOffsetVisibleColumn = visibleColumn; + } + + const charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); + visibleColumn += charWidth; + + if (visibleColumn > breakingColumn) { + // We need to break at least before character at `i`: + + if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { + // Cannot break at `breakOffset`, must break at `i` + breakOffset = i; + breakOffsetVisibleColumn = visibleColumn - charWidth; + } + + mustBreak = true; + break; + } + + prevCharCode = charCode; + prevCharCodeClass = charCodeClass; + } + + if (!mustBreak) { + // there is no more need to break => stop the outer loop! + // Add last segment + breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1]; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1]; + break; + } + + breakingOffsets[breakingOffsetsCount] = breakOffset; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; + breakingOffsetsCount++; + breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; + prevIndex++; + } else if (breakOffsetVisibleColumn > breakingColumn) { + const initialBreakOffset = breakOffset; + let visibleColumn = breakOffsetVisibleColumn; + breakOffset = 0; + + let charCode = lineText.charCodeAt(initialBreakOffset); + let charCodeClass = classifier.get(charCode); + let hitTab = false; + + let firstValidBreakOffset = 0; + let firstValidBreakOffsetVisibleColumn = 0; + for (let i = initialBreakOffset - 1; i >= 0; i--) { + let prevCharCode = lineText.charCodeAt(i); + let prevCharCodeClass = classifier.get(prevCharCode); + + if (strings.isHighSurrogate(prevCharCode)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + visibleColumn -= 1; + charCode = prevCharCode; + charCodeClass = prevCharCodeClass; + continue; + } + + if (prevCharCode === CharCode.Tab) { + // cannot determine the width of a tab when going backwards, so we must go forwards + hitTab = true; + break; + } + + const charWidth = (strings.isFullWidthCharacter(prevCharCode) ? columnsForFullWidthChar : 1); + + if (visibleColumn <= breakingColumn) { + if (firstValidBreakOffset === 0) { + firstValidBreakOffset = i + 1; + firstValidBreakOffsetVisibleColumn = visibleColumn; + } + + if (visibleColumn <= breakingColumn - wrappedLineBreakingColumn) { + // went too far! + break; + } + + if (canBreak(prevCharCodeClass, charCodeClass)) { + breakOffset = i + 1; + breakOffsetVisibleColumn = visibleColumn; + break; + } + } + + visibleColumn -= charWidth; + charCode = prevCharCode; + charCodeClass = prevCharCodeClass; + } + + if (hitTab) { + // cannot determine the width of a tab when going backwards, so we must go forwards + prevIndex--; + continue; + } + + if (breakOffset === 0) { + // Could not find a good breaking point + breakOffset = firstValidBreakOffset; + breakOffsetVisibleColumn = firstValidBreakOffsetVisibleColumn; + } + + breakingOffsets[breakingOffsetsCount] = breakOffset; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; + breakingOffsetsCount++; + breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; + } + + if (prevIndex < 0) { + prevIndex = 0; + } else { + let currentDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); + while (prevIndex + 1 < prevLen) { + const potentialDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); + if (potentialDiff >= currentDiff) { + break; + } + currentDiff = potentialDiff; + prevIndex++; + } + } + } + + if (breakingOffsetsCount === 0) { + return null; + } + + return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); + const expected = createLineMapping(classifier, null, lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); + const actual = new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); + try { + actual.assertEqual(expected); + } catch (err) { + console.log(`BREAKING!!`); + console.log(err); + console.log(` + assertIncrementalLineMapping( + factory, ${str(lineText)}, 4, + ${previousBreakingData.breakingColumn}, ${str(toAnnotatedText(lineText, previousBreakingData))}, + ${expected!.breakingColumn}, ${str(toAnnotatedText(lineText, expected))} + ); +`); + function str(strr: string) { + return `'${strr.replace(/'/g, '\\\'')}'`; + } + function toAnnotatedText(text: string, lineBreakingData: LineBreakingData | null): string { + // Insert line break markers again, according to algorithm + let actualAnnotatedText = ''; + if (lineBreakingData) { + let previousLineIndex = 0; + for (let i = 0, len = text.length; i < len; i++) { + let r = LineBreakingData.getOutputPositionOfInputOffset(lineBreakingData.breakOffsets, i); + if (previousLineIndex !== r.outputLineIndex) { + previousLineIndex = r.outputLineIndex; + actualAnnotatedText += '|'; + } + actualAnnotatedText += text.charAt(i); + } + } else { + // No wrapping + actualAnnotatedText = text; + } + return actualAnnotatedText; + } + } + return actual; + + breakingOffsets = []; + breakingOffsetsVisibleColumn = []; + breakingOffsetsCount = 0; + } + let breakOffset = 0; let breakOffsetVisibleColumn = 0; @@ -165,6 +381,62 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); } +// class BreakSearchResult { + +// public static INSTANCE = new BreakSearchResult(); + +// prevCharCode: number = CharCode.Null; +// prevCharCodeClass: CharacterClass = CharacterClass.NONE; +// breakOffset: number = 0; +// breakOffsetVisibleColumn: number = 0; +// visibleColumn: number = 0; +// } + +// function searchForBreak(classifier: WrappingCharacterClassifier, lineText: string, len: number, prevCharCode: number, prevCharCodeClass: number): boolean { +// let breakOffset = 0; +// let breakOffsetVisibleColumn = 0; +// for (let i = 1; i < len; i++) { +// const charCode = lineText.charCodeAt(i); +// const charCodeClass = classifier.get(charCode); + +// if (strings.isHighSurrogate(prevCharCode)) { +// // A surrogate pair must always be considered as a single unit, so it is never to be broken +// visibleColumn += 1; +// prevCharCode = charCode; +// prevCharCodeClass = charCodeClass; +// continue; +// } + +// if (canBreak(prevCharCodeClass, charCodeClass)) { +// breakOffset = i; +// breakOffsetVisibleColumn = visibleColumn; +// } + +// const charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); +// visibleColumn += charWidth; + +// // check if adding character at `i` will go over the breaking column +// if (visibleColumn > breakingColumn) { +// // We need to break at least before character at `i`: + +// if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { +// // Cannot break at `breakOffset`, must break at `i` +// breakOffset = i; +// breakOffsetVisibleColumn = visibleColumn - charWidth; +// } + +// breakingOffsets[breakingOffsetsCount] = breakOffset; +// breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; +// breakingOffsetsCount++; +// breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; +// breakOffset = 0; +// } + +// prevCharCode = charCode; +// prevCharCodeClass = charCodeClass; +// } +// } + function computeCharWidth(charCode: number, visibleColumn: number, tabSize: number, columnsForFullWidthChar: number): number { if (charCode === CharCode.Tab) { return (tabSize - (visibleColumn % tabSize)); diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 6346d5bbc4c..2f0af80ac24 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -34,6 +34,29 @@ export class LineBreakingData { public readonly wrappedTextIndentLength: number ) { } + assertEqual(other: LineBreakingData | null): void { + if (other === null) { + throw new Error(`x--unexpected--1`); + } + if (other.breakingColumn !== this.breakingColumn) { + throw new Error(`x--unexpected--2`); + } + if (other.wrappedTextIndentLength !== this.wrappedTextIndentLength) { + throw new Error(`x--unexpected--3`); + } + if (other.breakOffsets.length !== this.breakOffsets.length) { + throw new Error(`x--unexpected--4`); + } + for (let i = 0; i < this.breakOffsets.length; i++) { + if (this.breakOffsets[i] !== other.breakOffsets[i]) { + throw new Error(`x--unexpected--5`); + } + if (this.breakingOffsetsVisibleColumn[i] !== other.breakingOffsetsVisibleColumn[i]) { + throw new Error(`x--unexpected--6`); + } + } + } + public static getInputOffsetOfOutputPosition(breakOffsets: number[], outputLineIndex: number, outputOffset: number): number { if (outputLineIndex === 0) { return outputOffset; diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts index a1aec876698..4890907d5f9 100644 --- a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts +++ b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts @@ -3,49 +3,60 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; +import { WrappingIndent, EditorOptions } from 'vs/editor/common/config/editorOptions'; import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; import { ILineMapperFactory, LineBreakingData } from 'vs/editor/common/viewModel/splitLinesCollection'; -function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None): LineBreakingData | null { - // Create version of `annotatedText` with line break markers removed - let rawText = ''; +function parseAnnotatedText(annotatedText: string): { text: string; indices: number[]; } { + let text = ''; let currentLineIndex = 0; - let lineIndices: number[] = []; + let indices: number[] = []; for (let i = 0, len = annotatedText.length; i < len; i++) { if (annotatedText.charAt(i) === '|') { currentLineIndex++; } else { - rawText += annotatedText.charAt(i); - lineIndices[rawText.length - 1] = currentLineIndex; + text += annotatedText.charAt(i); + indices[text.length - 1] = currentLineIndex; } } + return { text: text, indices: indices }; +} - const lineMappingComputer = factory.createLineMappingComputer(tabSize, breakAfter, 2, wrappingIndent); - lineMappingComputer.addRequest(rawText, null); - const lineMappings = lineMappingComputer.finalize(); - const mapper = lineMappings[0]; - +function toAnnotatedText(text: string, lineBreakingData: LineBreakingData | null): string { // Insert line break markers again, according to algorithm let actualAnnotatedText = ''; - if (mapper) { + if (lineBreakingData) { let previousLineIndex = 0; - for (let i = 0, len = rawText.length; i < len; i++) { - let r = LineBreakingData.getOutputPositionOfInputOffset(mapper.breakOffsets, i); + for (let i = 0, len = text.length; i < len; i++) { + let r = LineBreakingData.getOutputPositionOfInputOffset(lineBreakingData.breakOffsets, i); if (previousLineIndex !== r.outputLineIndex) { previousLineIndex = r.outputLineIndex; actualAnnotatedText += '|'; } - actualAnnotatedText += rawText.charAt(i); + actualAnnotatedText += text.charAt(i); } } else { // No wrapping - actualAnnotatedText = rawText; + actualAnnotatedText = text; } + return actualAnnotatedText; +} + +function getLineBreakingData(factory: ILineMapperFactory, tabSize: number, breakAfter: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, text: string, previousLineBreakingData: LineBreakingData | null): LineBreakingData | null { + const lineMappingComputer = factory.createLineMappingComputer(tabSize, breakAfter, columnsForFullWidthChar, wrappingIndent); + lineMappingComputer.addRequest(text, previousLineBreakingData); + return lineMappingComputer.finalize()[0]; +} + +function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None): LineBreakingData | null { + // Create version of `annotatedText` with line break markers removed + const text = parseAnnotatedText(annotatedText).text; + const lineBreakingData = getLineBreakingData(factory, tabSize, breakAfter, 2, wrappingIndent, text, null); + const actualAnnotatedText = toAnnotatedText(text, lineBreakingData); assert.equal(actualAnnotatedText, annotatedText); - return mapper; + return lineBreakingData; } suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { @@ -91,6 +102,54 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { assertLineMapping(factory, 4, 5, 'aa.(.|).aaa'); }); + function assertIncrementalLineMapping(factory: ILineMapperFactory, text: string, tabSize: number, breakAfter1: number, annotatedText1: string, breakAfter2: number, annotatedText2: string, wrappingIndent = WrappingIndent.None): void { + // sanity check the test + assert.equal(text, parseAnnotatedText(annotatedText1).text); + assert.equal(text, parseAnnotatedText(annotatedText2).text); + + // check that the direct mapping is ok for 1 + const directLineBreakingData1 = getLineBreakingData(factory, tabSize, breakAfter1, 2, wrappingIndent, text, null); + assert.equal(toAnnotatedText(text, directLineBreakingData1), annotatedText1); + + // check that the direct mapping is ok for 2 + const directLineBreakingData2 = getLineBreakingData(factory, tabSize, breakAfter2, 2, wrappingIndent, text, null); + assert.equal(toAnnotatedText(text, directLineBreakingData2), annotatedText2); + + // check that going from 1 to 2 is ok + const lineBreakingData2from1 = getLineBreakingData(factory, tabSize, breakAfter2, 2, wrappingIndent, text, directLineBreakingData1); + assert.equal(toAnnotatedText(text, lineBreakingData2from1), annotatedText2); + assert.deepEqual(lineBreakingData2from1, directLineBreakingData2); + + // check that going from 2 to 1 is ok + const lineBreakingData1from2 = getLineBreakingData(factory, tabSize, breakAfter1, 2, wrappingIndent, text, directLineBreakingData2); + assert.equal(toAnnotatedText(text, lineBreakingData1from2), annotatedText1); + assert.deepEqual(lineBreakingData1from2, directLineBreakingData1); + } + + test('CharacterHardWrappingLineMapper incremental 1', () => { + + let factory = new CharacterHardWrappingLineMapperFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); + + assertIncrementalLineMapping( + factory, 'just some text and more', 4, + 10, 'just some |text and |more', + 15, 'just some text |and more' + ); + + assertIncrementalLineMapping( + factory, 'Cu scripserit suscipiantur eos, in affert pericula contentiones sed, cetero sanctus et pro. Ius vidit magna regione te, sit ei elaboraret liberavisse. Mundi verear eu mea, eam vero scriptorem in, vix in menandri assueverit. Natum definiebas cu vim. Vim doming vocibus efficiantur id. In indoctum deseruisse voluptatum vim, ad debitis verterem sed.', 4, + 47, 'Cu scripserit suscipiantur eos, in affert |pericula contentiones sed, cetero sanctus et |pro. Ius vidit magna regione te, sit ei |elaboraret liberavisse. Mundi verear eu mea, |eam vero scriptorem in, vix in menandri |assueverit. Natum definiebas cu vim. Vim |doming vocibus efficiantur id. In indoctum |deseruisse voluptatum vim, ad debitis verterem |sed.', + 142, 'Cu scripserit suscipiantur eos, in affert pericula contentiones sed, cetero sanctus et pro. Ius vidit magna regione te, sit ei elaboraret |liberavisse. Mundi verear eu mea, eam vero scriptorem in, vix in menandri assueverit. Natum definiebas cu vim. Vim doming vocibus efficiantur |id. In indoctum deseruisse voluptatum vim, ad debitis verterem sed.', + ); + + assertIncrementalLineMapping( + factory, 'An his legere persecuti, oblique delicata efficiantur ex vix, vel at graecis officiis maluisset. Et per impedit voluptua, usu discere maiorum at. Ut assum ornatus temporibus vis, an sea melius pericula. Ea dicunt oblique phaedrum nam, eu duo movet nobis. His melius facilis eu, vim malorum temporibus ne. Nec no sale regione, meliore civibus placerat id eam. Mea alii fabulas definitionem te, agam volutpat ad vis, et per bonorum nonumes repudiandae.', 4, + 57, 'An his legere persecuti, oblique delicata efficiantur ex |vix, vel at graecis officiis maluisset. Et per impedit |voluptua, usu discere maiorum at. Ut assum ornatus |temporibus vis, an sea melius pericula. Ea dicunt |oblique phaedrum nam, eu duo movet nobis. His melius |facilis eu, vim malorum temporibus ne. Nec no sale |regione, meliore civibus placerat id eam. Mea alii |fabulas definitionem te, agam volutpat ad vis, et per |bonorum nonumes repudiandae.', + 58, 'An his legere persecuti, oblique delicata efficiantur ex |vix, vel at graecis officiis maluisset. Et per impedit |voluptua, usu discere maiorum at. Ut assum ornatus |temporibus vis, an sea melius pericula. Ea dicunt oblique |phaedrum nam, eu duo movet nobis. His melius facilis eu, |vim malorum temporibus ne. Nec no sale regione, meliore |civibus placerat id eam. Mea alii fabulas definitionem te,| agam volutpat ad vis, et per bonorum nonumes repudiandae.' + ); + }); + + test('CharacterHardWrappingLineMapper - CJK and Kinsoku Shori', () => { let factory = new CharacterHardWrappingLineMapperFactory('(', '\t)'); assertLineMapping(factory, 4, 5, 'aa \u5b89|\u5b89'); From 4c7431ca8d6e48bdc5ff9e6ebc7c55dd379e3188 Mon Sep 17 00:00:00 2001 From: Robo Date: Thu, 9 Jan 2020 19:16:52 +0530 Subject: [PATCH 079/843] chore: Upgrade to electron 7.x (#83796) * chore: bump electron@7.0.0 * chore: update api * chore: Bump electron@7.0.1 * chore: bump electron@7.1.0 * chore: Bump electron@7.1.1 * chore: Bump electron@7.1.2 * FIXME: Skip webview tests that have improper shutdown path * chore: Bump electron@7.1.3 * bump electron@7.1.5 * debug test failures * chore: bump electron@7.1.7 * skip test for https://github.com/microsoft/vscode/issues/87330 Co-authored-by: Benjamin Pasero --- .yarnrc | 2 +- cgmanifest.json | 12 +- .../src/singlefolder-tests/webview.test.ts | 2 +- .../src/singlefolder-tests/window.test.ts | 17 +- package.json | 2 +- src/vs/code/electron-main/app.ts | 34 +- src/vs/code/electron-main/window.ts | 6 +- .../platform/dialogs/electron-main/dialogs.ts | 2 +- .../platform/driver/electron-main/driver.ts | 3 +- .../electron-main/electronMainService.ts | 16 +- .../electron-browser/webviewElement.ts | 6 +- src/vs/workbench/electron-browser/window.ts | 6 +- yarn.lock | 542 ++++++++++-------- 13 files changed, 374 insertions(+), 276 deletions(-) diff --git a/.yarnrc b/.yarnrc index 85baaa63a78..2c769cfba18 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "6.1.6" +target "7.1.7" runtime "electron" diff --git a/cgmanifest.json b/cgmanifest.json index c102a04f705..e9dfd3d6ef3 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "91f08db83c2ce8c722ddf0911ead8f7c473bedfa" + "commitHash": "e4745133a1d3745f066e068b8033c6a269b59caf" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "76.0.3809.146" + "version": "78.0.3904.130" }, { "component": { @@ -48,11 +48,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "64219741218aa87e259cf8257596073b8e747f0a" + "commitHash": "787378879acfb212ed4ff824bf9f767a24a5cb43a" } }, "isOnlyProductionDependency": true, - "version": "12.4.0" + "version": "12.8.1" }, { "component": { @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "19c705ab80cd6fdccca3d65803ec2c4addb9540a" + "commitHash": "bef0dd868b7d6d32716c319664ed480f2ae17396" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "6.1.6" + "version": "7.1.7" }, { "component": { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts index e785f1d4afb..e149da8995b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -13,7 +13,7 @@ const webviewId = 'myWebview'; const testDocument = join(vscode.workspace.rootPath || '', './bower.json'); -suite('Webview tests', () => { +suite.skip('Webview tests', () => { const disposables: vscode.Disposable[] = []; function _register(disposable: T) { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index c2ec4f13681..1e6d2f60669 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -145,17 +145,24 @@ suite('window namespace tests', () => { }); }); - test('active editor not always correct... #49125', async function () { + test.skip('active editor not always correct... #49125', async function () { + const randomFile1 = await createRandomFile(); + const randomFile2 = await createRandomFile(); + + console.log('Created random files: ' + randomFile1.toString() + ' and ' + randomFile2.toString()); + const [docA, docB] = await Promise.all([ - workspace.openTextDocument(await createRandomFile()), - workspace.openTextDocument(await createRandomFile()), + workspace.openTextDocument(randomFile1), + workspace.openTextDocument(randomFile2) ]); for (let c = 0; c < 4; c++) { let editorA = await window.showTextDocument(docA, ViewColumn.One); - assert(window.activeTextEditor === editorA); + console.log('Showing: ' + editorA.document.fileName + ' and active editor is: ' + window.activeTextEditor?.document.fileName); + assert.equal(window.activeTextEditor, editorA); let editorB = await window.showTextDocument(docB, ViewColumn.Two); - assert(window.activeTextEditor === editorB); + console.log('Showing: ' + editorB.document.fileName + ' and active editor is: ' + window.activeTextEditor?.document.fileName); + assert.equal(window.activeTextEditor, editorB); } }); diff --git a/package.json b/package.json index aa000ded9df..11d823edf02 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "coveralls": "^2.11.11", "cson-parser": "^1.3.3", "debounce": "^1.0.0", - "electron": "6.1.6", + "electron": "7.1.7", "eslint": "6.8.0", "eslint-plugin-jsdoc": "^19.1.0", "event-stream": "3.3.4", diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 4d544450cc8..549c348240b 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -171,7 +171,7 @@ export class CodeApplication extends Disposable { app.on('web-contents-created', (_event: Event, contents) => { contents.on('will-attach-webview', (event: Event, webPreferences, params) => { - const isValidWebviewSource = (source: string): boolean => { + const isValidWebviewSource = (source: string | undefined): boolean => { if (!source) { return false; } @@ -191,11 +191,11 @@ export class CodeApplication extends Disposable { webPreferences.nodeIntegration = false; // Verify URLs being loaded - if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) { + if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preload)) { return; } - delete webPreferences.preloadUrl; + delete (webPreferences as { preloadURL: string }).preloadURL; // https://github.com/electron/electron/issues/21553 // Otherwise prevent loading this.logService.error('webContents#web-contents-created: Prevented webview attach'); @@ -497,27 +497,27 @@ export class CodeApplication extends Disposable { this.logService.info(`Tracing: waiting for windows to get ready...`); let recordingStopped = false; - const stopRecording = (timeout: boolean) => { + const stopRecording = async (timeout: boolean) => { if (recordingStopped) { return; } recordingStopped = true; // only once - contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`), path => { - if (!timeout) { - if (this.dialogMainService) { - this.dialogMainService.showMessageBox({ - type: 'info', - message: localize('trace.message', "Successfully created trace."), - detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path), - buttons: [localize('trace.ok', "Ok")] - }, withNullAsUndefined(BrowserWindow.getFocusedWindow())); - } - } else { - this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`); + const path = await contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`)); + + if (!timeout) { + if (this.dialogMainService) { + this.dialogMainService.showMessageBox({ + type: 'info', + message: localize('trace.message', "Successfully created trace."), + detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path), + buttons: [localize('trace.ok', "Ok")] + }, withNullAsUndefined(BrowserWindow.getFocusedWindow())); } - }); + } else { + this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`); + } }; // Wait up to 30s before creating the trace anyways diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index d0ea8752aae..fadf9730016 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -347,9 +347,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { }); this._win.webContents.session.webRequest.onHeadersReceived(null!, (details, callback) => { - const responseHeaders = details.responseHeaders as { [key: string]: string[] }; + const responseHeaders = details.responseHeaders as Record; - const contentType: string[] = (responseHeaders['content-type'] || responseHeaders['Content-Type']); + const contentType = (responseHeaders['content-type'] || responseHeaders['Content-Type']); if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { return callback({ cancel: true }); } @@ -441,7 +441,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Inject headers when requests are incoming const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => - this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined } }))); + this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as Record }))); } private onWindowError(error: WindowError): void { diff --git a/src/vs/platform/dialogs/electron-main/dialogs.ts b/src/vs/platform/dialogs/electron-main/dialogs.ts index 7b49ca50c2e..c694c003e9b 100644 --- a/src/vs/platform/dialogs/electron-main/dialogs.ts +++ b/src/vs/platform/dialogs/electron-main/dialogs.ts @@ -173,7 +173,7 @@ export class DialogMainService implements IDialogMainService { showOpenDialog(options: OpenDialogOptions, window?: BrowserWindow): Promise { - function normalizePaths(paths: string[] | undefined): string[] | undefined { + function normalizePaths(paths: string[]): string[] { if (paths && paths.length > 0 && isMacintosh) { paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS } diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index e0beffc55ed..bae55607623 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -18,7 +18,6 @@ import { ScanCodeBinding } from 'vs/base/common/scanCode'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { timeout } from 'vs/base/common/async'; import { IDriver, IElement, IWindowDriver } from 'vs/platform/driver/common/driver'; -import { NativeImage } from 'electron'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IElectronMainService } from 'vs/platform/electron/electron-main/electronMainService'; @@ -67,7 +66,7 @@ export class Driver implements IDriver, IWindowDriverRegistry { throw new Error('Invalid window'); } const webContents = window.win.webContents; - const image = await new Promise(c => webContents.capturePage(c)); + const image = await webContents.capturePage(); return image.toPNG().toString('base64'); } diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts index 24ef0a029ab..98b0e2a1f66 100644 --- a/src/vs/platform/electron/electron-main/electronMainService.ts +++ b/src/vs/platform/electron/electron-main/electronMainService.ts @@ -367,15 +367,13 @@ export class ElectronMainService implements IElectronMainService { //#region Connectivity async resolveProxy(windowId: number | undefined, url: string): Promise { - return new Promise(resolve => { - const window = this.windowById(windowId); - const session = window?.win?.webContents?.session; - if (session) { - session.resolveProxy(url, proxy => resolve(proxy)); - } else { - resolve(); - } - }); + const window = this.windowById(windowId); + const session = window?.win?.webContents?.session; + if (session) { + return session.resolveProxy(url); + } else { + return undefined; + } } //#endregion diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 929b367097b..00aa8576189 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { FindInPageOptions, OnBeforeRequestDetails, OnHeadersReceivedDetails, Response, WebContents, WebviewTag } from 'electron'; +import { FindInPageOptions, OnBeforeRequestListenerDetails, OnHeadersReceivedListenerDetails, Response, WebContents, WebviewTag } from 'electron'; import { addDisposableListener } from 'vs/base/browser/dom'; import { Emitter, Event } from 'vs/base/common/event'; import { once } from 'vs/base/common/functional'; @@ -65,8 +65,8 @@ class WebviewTagHandle extends Disposable { } } -type OnBeforeRequestDelegate = (details: OnBeforeRequestDetails) => Promise; -type OnHeadersReceivedDelegate = (details: OnHeadersReceivedDetails) => { cancel: boolean; } | undefined; +type OnBeforeRequestDelegate = (details: OnBeforeRequestListenerDetails) => Promise; +type OnHeadersReceivedDelegate = (details: OnHeadersReceivedListenerDetails) => { cancel: boolean; } | undefined; class WebviewSession extends Disposable { diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 18166cdef37..8b2f1b17d6b 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -21,7 +21,7 @@ import * as browser from 'vs/base/browser/browser'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IResourceInput } from 'vs/platform/editor/common/editor'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; -import { ipcRenderer as ipc, webFrame, crashReporter, Event as IpcEvent } from 'electron'; +import { ipcRenderer as ipc, webFrame, crashReporter, CrashReporterStartOptions, Event as IpcEvent } from 'electron'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -537,13 +537,13 @@ export class ElectronWindow extends Disposable { } // base options with product info - const options = { + const options: CrashReporterStartOptions = { companyName, productName, submitURL: isWindows ? hockeyAppConfig[process.arch === 'ia32' ? 'win32-ia32' : 'win32-x64'] : isLinux ? hockeyAppConfig[`linux-x64`] : hockeyAppConfig.darwin, extra: { vscode_version: product.version, - vscode_commit: product.commit + vscode_commit: product.commit || '' } }; diff --git a/yarn.lock b/yarn.lock index e4ca9c5cc9c..36fee127c84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -95,6 +95,33 @@ lodash "^4.17.11" to-fast-properties "^2.0.0" +"@electron/get@^1.0.1": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.7.2.tgz#286436a9fb56ff1a1fcdf0e80131fd65f4d1e0fd" + integrity sha512-LSE4LZGMjGS9TloDx0yO44D2UTbaeKRk+QjlhWLiQlikV6J4spgDCjb6z4YIcqmPAwNzlNCnWF4dubytwI+ATA== + dependencies: + debug "^4.1.1" + env-paths "^2.2.0" + fs-extra "^8.1.0" + got "^9.6.0" + sanitize-filename "^1.6.2" + sumchecker "^3.0.1" + optionalDependencies: + global-agent "^2.0.2" + global-tunnel-ng "^2.7.1" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + "@types/applicationinsights@0.20.0": version "0.20.0" resolved "https://registry.yarnpkg.com/@types/applicationinsights/-/applicationinsights-0.20.0.tgz#fa7b36dc954f635fa9037cad27c378446b1048fb" @@ -172,10 +199,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== -"@types/node@^10.12.18": - version "10.17.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.9.tgz#4f251a1ed77ac7ef09d456247d67fc8173f6b9da" - integrity sha512-+6VygF9LbG7Gaqeog2G7u1+RUcmo0q1rI+2ZxdIg2fAUngk5Vz9fOCHXdloNUOHEPd1EuuOpL5O0CdgN9Fx5UQ== +"@types/node@^12.0.12": + version "12.12.24" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.24.tgz#d4606afd8cf6c609036b854360367d1b2c78931f" + integrity sha512-1Ciqv9pqwVtW6FsIUKSZNB82E5Cu1I2bBTj1xuIHXLe/1zYLl3956Nbhg2MzSYHVfl9/rmanjbQIb7LibfCnug== "@types/node@^12.11.7": version "12.12.14" @@ -825,11 +852,6 @@ array-each@^1.0.0, array-each@^1.0.1: resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -1200,6 +1222,11 @@ boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +boolean@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.0.tgz#fab78d5907dbae6216ab46d32733bb7b76b99e76" + integrity sha512-OElxJ1lUSinuoUnkpOgLmxp0DC4ytEhODEL6QJU0NpxE/mI4rUSh8h1P1Wkvfi3xQEBcxXR2gBIPNYNuaFcAbQ== + boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" @@ -1444,24 +1471,24 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + callsites@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.0.0.tgz#fb7eb569b72ad7a45812f93fd9430a3e410b3dd3" integrity sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw== -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= - camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" @@ -1720,6 +1747,13 @@ clone-buffer@^1.0.0: resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + clone-stats@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" @@ -1947,7 +1981,7 @@ concat-with-sourcemaps@^1.0.0: dependencies: source-map "^0.5.1" -config-chain@^1.1.12: +config-chain@^1.1.11, config-chain@^1.1.12: version "1.1.12" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== @@ -2051,6 +2085,11 @@ copy-webpack-plugin@^4.5.2: p-limit "^1.0.0" serialize-javascript "^1.4.0" +core-js@^3.4.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.1.tgz#39d5e2e346258cc01eb7d44345b1c3c014ca3f05" + integrity sha512-186WjSik2iTGfDjfdCZAxv2ormxtKgemjC3SI6PL31qOA0j5LhTDVjHChccoc7brwLvpvLPiMyRlcO88C4l1QQ== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -2235,13 +2274,6 @@ cuint@^0.2.1: resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b" integrity sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs= -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= - dependencies: - array-find-index "^1.0.1" - cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" @@ -2278,7 +2310,7 @@ debug@2.2.0: dependencies: ms "0.7.1" -debug@2.6.9, debug@^2.1.2, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3: +debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -2292,7 +2324,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@^3.0.0, debug@^3.1.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== @@ -2363,7 +2395,12 @@ default-resolution@^2.0.0: resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684" integrity sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ= -define-properties@^1.1.2: +defer-to-connect@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.1.tgz#88ae694b93f67b81815a2c8c769aef6574ac8f2f" + integrity sha512-J7thop4u3mRTkYRQ+Vpfwy2G5Ehoy82I14+14W4YMDLKdWloI9gSzRbV30s/NckQGVJtPkWNcW4oMAUigTdqiQ== + +define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== @@ -2463,6 +2500,11 @@ detect-libc@^1.0.2, detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= +detect-node@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== + diagnostic-channel-publishers@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" @@ -2562,6 +2604,11 @@ domutils@^1.5.1: dom-serializer "0" domelementtype "1" +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + duplexer@^0.1.1, duplexer@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" @@ -2631,33 +2678,18 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-download@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.1.tgz#02e69556705cc456e520f9e035556ed5a015ebe8" - integrity sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg== - dependencies: - debug "^3.0.0" - env-paths "^1.0.0" - fs-extra "^4.0.1" - minimist "^1.2.0" - nugget "^2.0.1" - path-exists "^3.0.0" - rc "^1.2.1" - semver "^5.4.1" - sumchecker "^2.0.2" - electron-to-chromium@^1.2.7: version "1.3.27" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz#78ecb8a399066187bb374eede35d9c70565a803d" integrity sha1-eOy4o5kGYYe7N07t412ccFZagD0= -electron@6.1.6: - version "6.1.6" - resolved "https://registry.yarnpkg.com/electron/-/electron-6.1.6.tgz#d63ea9c89b85b981a29eb3088bf391bf52bd8d73" - integrity sha512-4c4GiFTbWY2Mgv20HB4Bfhf1hDKb0MWgC35wkwNepNom1ioWfumocPHZrSs1xNAEe+tOmezY6lq74n3LbwTnVQ== +electron@7.1.7: + version "7.1.7" + resolved "https://registry.yarnpkg.com/electron/-/electron-7.1.7.tgz#520e2bc422e3dfd4bae166dd3be62101f2cbdc52" + integrity sha512-aCLJ4BJwnvOckJgovNul22AYlMFDzm4S4KqKCG2iBlFJyMHBxXAKFKMsgYd40LBZWS3hcY6RHpaYjHSAPLS1pw== dependencies: - "@types/node" "^10.12.18" - electron-download "^4.1.0" + "@electron/get" "^1.0.1" + "@types/node" "^12.0.12" extract-zip "^1.0.3" elliptic@^6.0.0: @@ -2695,6 +2727,11 @@ emojis-list@^2.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= +encodeurl@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + encodeurl@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" @@ -2721,10 +2758,10 @@ entities@^1.1.1, entities@~1.1.1: resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA= -env-paths@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" - integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA= +env-paths@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" + integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== errno@^0.1.3, errno@~0.1.7: version "0.1.7" @@ -2748,6 +2785,11 @@ es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: es6-iterator "~2.0.1" es6-symbol "~3.1.1" +es6-error@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + es6-iterator@^2.0.1, es6-iterator@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" @@ -2802,6 +2844,11 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.3, escape-string-regexp@^ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + eslint-plugin-jsdoc@^19.1.0: version "19.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-19.1.0.tgz#fcc17f0378fdd6ee1c847a79b7211745cb05d014" @@ -3562,15 +3609,6 @@ fs-extra@0.26.7: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" - integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -3580,6 +3618,15 @@ fs-extra@^7.0.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" @@ -3687,18 +3734,20 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= - -get-stream@^4.0.0: +get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" +get-stream@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" + integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== + dependencies: + pump "^3.0.0" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -3864,6 +3913,19 @@ glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +global-agent@^2.0.2: + version "2.1.7" + resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.1.7.tgz#12d7bc2b07cd862d0fa76b0f1b2c48cd5ffcf150" + integrity sha512-ooK7eqGYZku+LgnbfH/Iv0RJ74XfhrBZDlke1QSzcBt0bw1PmJcnRADPAQuFE+R45pKKDTynAr25SBasY2kvow== + dependencies: + boolean "^3.0.0" + core-js "^3.4.1" + es6-error "^4.1.1" + matcher "^2.0.0" + roarr "^2.14.5" + semver "^6.3.0" + serialize-error "^5.0.0" + global-modules@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" @@ -3900,6 +3962,16 @@ global-prefix@^3.0.0: kind-of "^6.0.2" which "^1.3.1" +global-tunnel-ng@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f" + integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg== + dependencies: + encodeurl "^1.0.2" + lodash "^4.17.10" + npm-conf "^1.1.3" + tunnel "^0.0.6" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -3917,6 +3989,13 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globalthis@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.1.tgz#40116f5d9c071f9e8fb0037654df1ab3a83b7ef9" + integrity sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw== + dependencies: + define-properties "^1.1.3" + globby@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" @@ -3948,11 +4027,33 @@ glogg@^1.0.0: dependencies: sparkles "^1.0.0" +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + graceful-fs@4.1.11, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= +graceful-fs@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" + integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== + growl@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" @@ -4416,6 +4517,11 @@ html-comment-regex@^1.1.0: inherits "^2.0.1" readable-stream "^2.0.2" +http-cache-semantics@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz#495704773277eeef6e43f9ab2c2c7d259dda25c5" + integrity sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew== + http-errors@1.6.2, http-errors@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" @@ -4549,13 +4655,6 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" - indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" @@ -4811,13 +4910,6 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" @@ -5214,6 +5306,11 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + json-edm-parser@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/json-edm-parser/-/json-edm-parser-0.1.2.tgz#1e60b0fef1bc0af67bc0d146dfdde5486cd615b4" @@ -5253,7 +5350,7 @@ json-stable-stringify@^1.0.0: dependencies: jsonify "~0.0.0" -json-stringify-safe@~5.0.1: +json-stringify-safe@^5.0.1, 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= @@ -5322,6 +5419,13 @@ keytar@^4.11.0: nan "2.14.0" prebuild-install "5.3.0" +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + kind-of@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44" @@ -5595,13 +5699,15 @@ long@^3.2.0: resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" integrity sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s= -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== lru-cache@2: version "2.7.3" @@ -5668,11 +5774,6 @@ map-cache@^0.2.0, map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= - map-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.7.tgz#8a1f07896d82b10926bd3744a2420009f88974a8" @@ -5711,6 +5812,13 @@ matchdep@^2.0.0: resolve "^1.4.0" stack-trace "0.0.10" +matcher@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/matcher/-/matcher-2.1.0.tgz#64e1041c15b993e23b786f93320a7474bf833c28" + integrity sha512-o+nZr+vtJtgPNklyeUKkkH42OsK8WAfdgaJE2FNxcjLPg+5QbeEoT6vRj8Xq/iv18JlQ9cmKsEu0b94ixWf1YQ== + dependencies: + escape-string-regexp "^2.0.0" + math-expression-evaluator@^1.2.14: version "1.2.17" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" @@ -5760,22 +5868,6 @@ memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: errno "^0.1.3" readable-stream "^2.0.1" -meow@^3.1.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -5895,6 +5987,11 @@ mimic-response@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e" integrity sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4= +mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -5925,7 +6022,7 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: +minimist@1.2.0, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= @@ -6269,16 +6366,6 @@ normalize-package-data@^2.3.2: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-package-data@^2.3.4: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - normalize-path@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379" @@ -6316,6 +6403,11 @@ normalize-url@^1.4.0: query-string "^4.1.0" sort-keys "^1.0.0" +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + now-and-later@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.0.tgz#bc61cbb456d79cb32207ce47ca05136ff2e7d6ee" @@ -6328,6 +6420,14 @@ npm-bundled@^1.0.1: resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" integrity sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow== +npm-conf@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" + integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== + dependencies: + config-chain "^1.1.11" + pify "^3.0.0" + npm-packlist@^1.1.6: version "1.1.11" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.11.tgz#84e8c683cbe7867d34b1d357d893ce29e28a02de" @@ -6360,19 +6460,6 @@ nth-check@~1.0.1: dependencies: boolbase "~1.0.0" -nugget@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/nugget/-/nugget-2.0.1.tgz#201095a487e1ad36081b3432fa3cada4f8d071b0" - integrity sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA= - dependencies: - debug "^2.1.3" - minimist "^1.1.0" - pretty-bytes "^1.0.2" - progress-stream "^1.1.0" - request "^2.45.0" - single-line-log "^1.1.2" - throttleit "0.0.2" - num2fraction@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" @@ -6640,6 +6727,11 @@ p-all@^1.0.0: dependencies: p-map "^1.0.0" +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -7263,19 +7355,16 @@ prepend-http@^1.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= -pretty-bytes@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-1.0.4.tgz#0a22e8210609ad35542f8c8d5d2159aff0751c84" - integrity sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ= - dependencies: - get-stdin "^4.0.1" - meow "^3.1.0" - pretty-hrtime@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -7304,14 +7393,6 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= -progress-stream@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/progress-stream/-/progress-stream-1.2.0.tgz#2cd3cfea33ba3a89c9c121ec3347abe9ab125f77" - integrity sha1-LNPP6jO6OonJwSHsM0er6asSX3c= - dependencies: - speedometer "~0.1.2" - through2 "~0.2.3" - progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" @@ -7519,7 +7600,7 @@ raw-body@2.3.2: iconv-lite "0.4.19" unpipe "1.0.0" -rc@^1.2.1, rc@^1.2.7: +rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -7595,7 +7676,7 @@ read@^1.0.7: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^1.1.8, readable-stream@~1.1.9: +readable-stream@^1.1.8: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= @@ -7663,14 +7744,6 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - reduce-css-calc@^1.2.6: version "1.3.0" resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" @@ -7750,13 +7823,6 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - replace-ext@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" @@ -7839,7 +7905,7 @@ request@2.79.0: tunnel-agent "^0.6.0" uuid "^3.1.0" -request@^2.45.0, request@^2.86.0, request@^2.88.0: +request@^2.86.0, 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== @@ -7933,13 +7999,6 @@ resolve@^1.1.6, resolve@^1.1.7: dependencies: path-parse "^1.0.5" -resolve@^1.10.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.13.1.tgz#be0aa4c06acd53083505abb35f4d66932ab35d16" - integrity sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w== - dependencies: - path-parse "^1.0.6" - resolve@^1.4.0: version "1.10.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" @@ -7947,6 +8006,13 @@ resolve@^1.4.0: dependencies: path-parse "^1.0.6" +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -8002,6 +8068,18 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +roarr@^2.14.5: + version "2.14.6" + resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.14.6.tgz#cebe8ad7ecbfd15bfa37b02dacf00809dd633912" + integrity sha512-qjbw0BEesKA+3XFBPt+KVe1PC/Z6ShfJ4wPlx2XifqH5h2Lj8/KQT5XJTsy3n1Es5kai+BwKALaECW3F70B1cg== + dependencies: + boolean "^3.0.0" + detect-node "^2.0.4" + globalthis "^1.0.0" + json-stringify-safe "^5.0.1" + semver-compare "^1.0.0" + sprintf-js "^1.1.2" + run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -8062,6 +8140,13 @@ samsam@~1.1: resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621" integrity sha1-n1CHQZtNCR8jJXHn+lLpCw9VJiE= +sanitize-filename@^1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378" + integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg== + dependencies: + truncate-utf8-bytes "^1.0.0" + sax@0.5.x: version "0.5.8" resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" @@ -8080,6 +8165,11 @@ schema-utils@^0.4.4, schema-utils@^0.4.5: ajv "^6.1.0" ajv-keywords "^3.1.0" +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= + semver-greatest-satisfied-range@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" @@ -8141,6 +8231,13 @@ send@0.16.1: range-parser "~1.2.0" statuses "~1.3.1" +serialize-error@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-5.0.0.tgz#a7ebbcdb03a5d71a6ed8461ffe0fc1a1afed62ac" + integrity sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA== + dependencies: + type-fest "^0.8.0" + serialize-javascript@^1.4.0: version "1.5.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.5.0.tgz#1aa336162c88a890ddad5384baebc93a655161fe" @@ -8250,13 +8347,6 @@ simple-get@^2.7.0: once "^1.3.1" simple-concat "^1.0.0" -single-line-log@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/single-line-log/-/single-line-log-1.1.2.tgz#c2f83f273a3e1a16edb0995661da0ed5ef033364" - integrity sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q= - dependencies: - string-width "^1.0.1" - sinon@^1.17.2: version "1.17.7" resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf" @@ -8440,11 +8530,6 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== -speedometer@~0.1.2: - version "0.1.4" - resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d" - integrity sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0= - split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -8466,6 +8551,11 @@ split@^1.0.1: dependencies: through "2" +sprintf-js@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" + integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -8711,13 +8801,6 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= - dependencies: - get-stdin "^4.0.1" - strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -8733,12 +8816,12 @@ sudo-prompt@9.1.1: resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.1.1.tgz#73853d729770392caec029e2470db9c221754db0" integrity sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA== -sumchecker@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-2.0.2.tgz#0f42c10e5d05da5d42eea3e56c3399a37d6c5b3e" - integrity sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4= +sumchecker@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" + integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== dependencies: - debug "^2.2.0" + debug "^4.1.0" supports-color@1.2.0: version "1.2.0" @@ -8887,11 +8970,6 @@ textextensions@~1.0.0: resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-1.0.2.tgz#65486393ee1f2bb039a60cbba05b0b68bd9501d2" integrity sha1-ZUhjk+4fK7A5pgy7oFsLaL2VAdI= -throttleit@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-0.0.2.tgz#cfedf88e60c00dd9697b61fdd2a8343a9b680eaf" - integrity sha1-z+34jmDADdlpe2H90qg0OptoDq8= - through2-filter@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-2.0.0.tgz#60bc55a0dacb76085db1f9dae99ab43f83d622ec" @@ -8924,14 +9002,6 @@ through2@^3.0.0: readable-stream "2 || 3" xtend "~4.0.1" -through2@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-0.2.3.tgz#eb3284da4ea311b6cc8ace3653748a52abf25a3f" - integrity sha1-6zKE2k6jEbbMis42U3SKUqvyWj8= - dependencies: - readable-stream "~1.1.9" - xtend "~2.1.1" - through2@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/through2/-/through2-0.4.2.tgz#dbf5866031151ec8352bb6c4db64a2292a840b9b" @@ -9020,6 +9090,11 @@ to-object-path@^0.3.0: dependencies: kind-of "^3.0.2" +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" @@ -9079,16 +9154,18 @@ tough-cookie@~2.4.3: resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk= -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= - trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= +truncate-utf8-bytes@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b" + integrity sha1-QFkjkJWS1W94pYGENLC3hInKXys= + dependencies: + utf8-byte-length "^1.0.1" + ts-loader@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-4.4.2.tgz#778d4464b24436873c78f7f9e914d88194c2a248" @@ -9134,6 +9211,11 @@ tunnel@0.0.4: resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.4.tgz#2d3785a158c174c9a16dc2c046ec5fc5f1742213" integrity sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM= +tunnel@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -9146,7 +9228,7 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-fest@^0.8.1: +type-fest@^0.8.0, type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== @@ -9349,6 +9431,13 @@ url-join@^1.1.0: resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78" integrity sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg= +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -9362,6 +9451,11 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== +utf8-byte-length@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61" + integrity sha1-9F8VDExm7uloGGUFq5P8u4rWv2E= + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" From 816064e3b334e200965864fd67caef6a6a4dd8fb Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 9 Jan 2020 15:08:27 +0100 Subject: [PATCH 080/843] Use arrow instead of 'to' in forwarded ports view Part of #86066 --- src/vs/workbench/contrib/remote/browser/tunnelView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 440a7ee8000..ec5fedf10f9 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -364,9 +364,9 @@ class TunnelItem implements ITunnelItem { if (this.name) { return nls.localize('remote.tunnelsView.forwardedPortLabel0', "{0}", this.name); } else if (this.localAddress && (this.remoteHost !== 'localhost')) { - return nls.localize('remote.tunnelsView.forwardedPortLabel2', "{0}:{1} to {2}", this.remoteHost, this.remotePort, this.localAddress); + return nls.localize('remote.tunnelsView.forwardedPortLabel2', "{0}:{1} \u2192 {2}", this.remoteHost, this.remotePort, this.localAddress); } else if (this.localAddress) { - return nls.localize('remote.tunnelsView.forwardedPortLabel3', "{0} to {1}", this.remotePort, this.localAddress); + return nls.localize('remote.tunnelsView.forwardedPortLabel3', "{0} \u2192 {1}", this.remotePort, this.localAddress); } else if (this.remoteHost !== 'localhost') { return nls.localize('remote.tunnelsView.forwardedPortLabel4', "{0}:{1} not forwarded", this.remoteHost, this.remotePort); } else { From a66c1f592dce65c3863cb09aff605ff2aabc8d03 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 9 Jan 2020 15:25:57 +0100 Subject: [PATCH 081/843] Error while computing semantic tokens. Fixes #88366 --- .../server/src/modes/javascriptSemanticTokens.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts index fa615319354..7e4deafa262 100644 --- a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts +++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts @@ -20,7 +20,7 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current if (node.kind === ts.SyntaxKind.Identifier) { const symbol = typeChecker.getSymbolAtLocation(node); if (symbol) { - const decl = symbol.valueDeclaration || symbol.declarations[0]; + const decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; if (decl) { let typeIdx = tokenFromDeclarationMapping[decl.kind]; let modifierSet = 0; From c40b6072bc10cb795a67f112feaa6d2bff6d7fb7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 9 Jan 2020 15:50:21 +0100 Subject: [PATCH 082/843] propose CompletionList#isDetailsResolved, #39441 --- src/vs/editor/common/modes.ts | 1 + src/vs/editor/contrib/suggest/suggest.ts | 5 +++++ src/vs/monaco.d.ts | 1 + src/vs/vscode.proposed.d.ts | 11 +++++++++++ .../api/browser/mainThreadLanguageFeatures.ts | 1 + src/vs/workbench/api/common/extHost.protocol.ts | 1 + .../workbench/api/common/extHostLanguageFeatures.ts | 3 ++- src/vs/workbench/api/common/extHostTypes.ts | 2 +- 8 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 9db95948d24..78495217dc3 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -481,6 +481,7 @@ export interface CompletionItem { export interface CompletionList { suggestions: CompletionItem[]; incomplete?: boolean; + isDetailsResolved?: boolean; dispose?(): void; } diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index d28423e1602..c2d3185ce7d 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -47,6 +47,9 @@ export class CompletionItem { idx?: number; word?: string; + // + readonly isDetailsResolved: boolean; + constructor( readonly position: IPosition, readonly completion: modes.CompletionItem, @@ -70,6 +73,8 @@ export class CompletionItem { this.editReplaceEnd = new Position(completion.range.replace.endLineNumber, completion.range.replace.endColumn); } + this.isDetailsResolved = container.isDetailsResolved || typeof provider.resolveCompletionItem === 'undefined'; + // create the suggestion resolver const { resolveCompletionItem } = provider; if (typeof resolveCompletionItem !== 'function') { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 32a3f131d6b..38597bc5d3b 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4854,6 +4854,7 @@ declare namespace monaco.languages { export interface CompletionList { suggestions: CompletionItem[]; incomplete?: boolean; + isDetailsResolved?: boolean; dispose?(): void; } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 15d667e14fc..27a17629b13 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1504,4 +1504,15 @@ declare module 'vscode' { export const onDidChangeActiveColorTheme: Event; } + //#endregion + + + //#region https://github.com/microsoft/vscode/issues/39441 + + export interface CompletionList { + isDetailsResolved?: boolean; + } + + //#endregion + } diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 0ae9f987450..da77e770f4e 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -367,6 +367,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha return { suggestions: result.b.map(d => MainThreadLanguageFeatures._inflateSuggestDto(result.a, d)), incomplete: result.c, + isDetailsResolved: result.d, dispose: () => typeof result.x === 'number' && this._proxy.$releaseCompletionItems(handle, result.x) }; }); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 076f847e2f6..227a885f876 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1012,6 +1012,7 @@ export interface ISuggestResultDto { a: { insert: IRange, replace: IRange; }; b: ISuggestDataDto[]; c?: boolean; + d?: boolean; } export interface ISignatureHelpDto { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index ba0f638fefd..737ed4be714 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -787,7 +787,8 @@ class SuggestAdapter { x: pid, b: [], a: { replace: typeConvert.Range.from(replaceRange), insert: typeConvert.Range.from(insertRange) }, - c: list.isIncomplete || undefined + c: list.isIncomplete || undefined, + d: list.isDetailsResolved || undefined }; for (let i = 0; i < list.items.length; i++) { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 19819497a1e..5239da9499b 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1395,7 +1395,7 @@ export class CompletionItem implements vscode.CompletionItem { export class CompletionList { isIncomplete?: boolean; - + isDetailsResolved?: boolean; items: vscode.CompletionItem[]; constructor(items: vscode.CompletionItem[] = [], isIncomplete: boolean = false) { From c7bd9c76f0bbf86594827e7bf034c2c384605727 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 9 Jan 2020 16:03:09 +0100 Subject: [PATCH 083/843] Better ipv6 display in forwarded ports view --- .../workbench/api/node/extHostTunnelService.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index ef2679b4f9d..a5932990726 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -190,10 +190,19 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe private parseIpAddress(hex: string): string { let result = ''; - for (let i = hex.length - 2; (i >= 0); i -= 2) { - result += parseInt(hex.substr(i, 2), 16); - if (i !== 0) { - result += '.'; + if (hex.length === 8) { + for (let i = hex.length - 2; i >= 0; i -= 2) { + result += parseInt(hex.substr(i, 2), 16); + if (i !== 0) { + result += '.'; + } + } + } else { + for (let i = hex.length - 4; i >= 0; i -= 4) { + result += parseInt(hex.substr(i, 4), 16).toString(16); + if (i !== 0) { + result += ':'; + } } } return result; From 99f0da6e5418879a5221099a164e96243709ede8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 9 Jan 2020 16:11:24 +0100 Subject: [PATCH 084/843] adopt isDetailsResolved for snippets, #39441 --- .../contrib/snippets/browser/snippetCompletionProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts b/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts index 96d9f026104..e30853b4c2c 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts @@ -146,7 +146,7 @@ export class SnippetCompletionProvider implements CompletionItemProvider { i = to; } } - return { suggestions }; + return { suggestions, isDetailsResolved: true }; }); } From 4d9cbf3bde1e1ea570d65b2b7414199833f8b61a Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 9 Jan 2020 16:22:59 +0100 Subject: [PATCH 085/843] wip --- .../characterHardWrappingLineMapper.ts | 157 ++++++++++-------- .../characterHardWrappingLineMapper.test.ts | 7 + 2 files changed, 92 insertions(+), 72 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 0790c9a6444..32b607c356e 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -111,31 +111,40 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakingOffsetsVisibleColumn; let breakingColumn = firstLineBreakingColumn; + let lastBreakingOffsetVisibleColumn = 0; const prevLen = prevBreakingOffsets.length; let prevIndex = 0; - while (prevIndex < prevLen) { - // Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break) - let breakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex]; - let breakOffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex]; - - if (breakOffsetVisibleColumn === breakingColumn) { - // perfect fit, nothing to do - breakingOffsets[breakingOffsetsCount] = breakOffset; - breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; - breakingOffsetsCount++; - breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; + if (prevIndex >= 0) { + let currentDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); + while (prevIndex + 1 < prevLen) { + const potentialDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); + if (potentialDiff >= currentDiff) { + break; + } + currentDiff = potentialDiff; prevIndex++; - } else if (breakOffsetVisibleColumn < breakingColumn) { - // try to add more characters - const initialBreakOffset = breakOffset; - let visibleColumn = breakOffsetVisibleColumn; - breakOffset = 0; + } + } - let prevCharCode = lineText.charCodeAt(initialBreakOffset - 1); + while (prevIndex < prevLen) { + // Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break) + const prevBreakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex]; + const prevBreakoffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex];; + + let breakOffset = 0; + let breakOffsetVisibleColumn = 0; + + let forcedBreakOffset = 0; + let forcedBreakOffsetVisibleColumn = 0; + + // initially, we search as much as possible to the right (if it fits) + if (prevBreakoffsetVisibleColumn <= breakingColumn) { + let visibleColumn = prevBreakoffsetVisibleColumn; + let prevCharCode = lineText.charCodeAt(prevBreakOffset - 1); let prevCharCodeClass = classifier.get(prevCharCode); - let mustBreak = false; - for (let i = initialBreakOffset; i < len; i++) { + let entireLineFits = true; + for (let i = prevBreakOffset; i < len; i++) { const charCode = lineText.charCodeAt(i); const charCodeClass = classifier.get(charCode); @@ -145,6 +154,7 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea prevCharCode = charCode; prevCharCodeClass = charCodeClass; continue; + } if (canBreak(prevCharCodeClass, charCodeClass)) { @@ -157,14 +167,15 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea if (visibleColumn > breakingColumn) { // We need to break at least before character at `i`: + forcedBreakOffset = i; + forcedBreakOffsetVisibleColumn = visibleColumn - charWidth; - if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { - // Cannot break at `breakOffset`, must break at `i` - breakOffset = i; - breakOffsetVisibleColumn = visibleColumn - charWidth; + if (visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { + // Cannot break at `breakOffset` => reset it if it was set + breakOffset = 0; } - mustBreak = true; + entireLineFits = false; break; } @@ -172,33 +183,24 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea prevCharCodeClass = charCodeClass; } - if (!mustBreak) { + if (entireLineFits) { // there is no more need to break => stop the outer loop! // Add last segment breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1]; breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1]; break; } + } - breakingOffsets[breakingOffsetsCount] = breakOffset; - breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; - breakingOffsetsCount++; - breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; - prevIndex++; - } else if (breakOffsetVisibleColumn > breakingColumn) { - const initialBreakOffset = breakOffset; - let visibleColumn = breakOffsetVisibleColumn; - breakOffset = 0; - - let charCode = lineText.charCodeAt(initialBreakOffset); + if (breakOffset === 0) { + // must search left + let visibleColumn = prevBreakoffsetVisibleColumn; + let charCode = lineText.charCodeAt(prevBreakOffset); let charCodeClass = classifier.get(charCode); - let hitTab = false; - - let firstValidBreakOffset = 0; - let firstValidBreakOffsetVisibleColumn = 0; - for (let i = initialBreakOffset - 1; i >= 0; i--) { - let prevCharCode = lineText.charCodeAt(i); - let prevCharCodeClass = classifier.get(prevCharCode); + let hitATabCharacter = false; + for (let i = prevBreakOffset - 1; i >= 0; i--) { + const prevCharCode = lineText.charCodeAt(i); + const prevCharCodeClass = classifier.get(prevCharCode); if (strings.isHighSurrogate(prevCharCode)) { // A surrogate pair must always be considered as a single unit, so it is never to be broken @@ -210,16 +212,16 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea if (prevCharCode === CharCode.Tab) { // cannot determine the width of a tab when going backwards, so we must go forwards - hitTab = true; + hitATabCharacter = true; break; } const charWidth = (strings.isFullWidthCharacter(prevCharCode) ? columnsForFullWidthChar : 1); if (visibleColumn <= breakingColumn) { - if (firstValidBreakOffset === 0) { - firstValidBreakOffset = i + 1; - firstValidBreakOffsetVisibleColumn = visibleColumn; + if (forcedBreakOffset === 0) { + forcedBreakOffset = i + 1; + forcedBreakOffsetVisibleColumn = visibleColumn; } if (visibleColumn <= breakingColumn - wrappedLineBreakingColumn) { @@ -239,36 +241,37 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea charCodeClass = prevCharCodeClass; } - if (hitTab) { - // cannot determine the width of a tab when going backwards, so we must go forwards + if (hitATabCharacter) { + // cannot determine the width of a tab when going backwards, so we must go forwards from the previous break prevIndex--; continue; } - - if (breakOffset === 0) { - // Could not find a good breaking point - breakOffset = firstValidBreakOffset; - breakOffsetVisibleColumn = firstValidBreakOffsetVisibleColumn; - } - - breakingOffsets[breakingOffsetsCount] = breakOffset; - breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; - breakingOffsetsCount++; - breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; } - if (prevIndex < 0) { - prevIndex = 0; - } else { - let currentDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); - while (prevIndex + 1 < prevLen) { - const potentialDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); - if (potentialDiff >= currentDiff) { - break; - } - currentDiff = potentialDiff; - prevIndex++; + if (breakOffset === 0) { + // Could not find a good breaking point + breakOffset = forcedBreakOffset; + breakOffsetVisibleColumn = forcedBreakOffsetVisibleColumn; + } + + breakingOffsets[breakingOffsetsCount] = breakOffset; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; + breakingOffsetsCount++; + breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; + + while (prevIndex < 0 || (prevIndex < prevLen && prevBreakingOffsetsVisibleColumn[prevIndex] < breakOffsetVisibleColumn)) { + prevIndex++; + } + + let currentDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); + // let lastBreakingColumn + while (prevIndex + 1 < prevLen) { + const potentialDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); + if (potentialDiff >= currentDiff) { + break; } + currentDiff = potentialDiff; + prevIndex++; } } @@ -284,11 +287,19 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea } catch (err) { console.log(`BREAKING!!`); console.log(err); + console.log(`previous breaks: ${JSON.stringify(prevBreakingOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(prevBreakingOffsetsVisibleColumn)}`); + console.log(`expected breakOffsets: ${JSON.stringify(expected?.breakOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(expected?.breakingOffsetsVisibleColumn)}`); + console.log(`actual breakOffsets: ${JSON.stringify(actual?.breakOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(actual?.breakingOffsetsVisibleColumn)}`); + + console.log(`actual str: ${toAnnotatedText(lineText, actual)}`); + + console.log(` assertIncrementalLineMapping( factory, ${str(lineText)}, 4, ${previousBreakingData.breakingColumn}, ${str(toAnnotatedText(lineText, previousBreakingData))}, - ${expected!.breakingColumn}, ${str(toAnnotatedText(lineText, expected))} + ${expected!.breakingColumn}, ${str(toAnnotatedText(lineText, expected))}, + WrappingIndent.${hardWrappingIndent === WrappingIndent.None ? 'None' : hardWrappingIndent === WrappingIndent.Same ? 'Same' : hardWrappingIndent === WrappingIndent.Indent ? 'Indent' : 'DeepIndent'} ); `); function str(strr: string) { @@ -381,6 +392,8 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); } +function + // class BreakSearchResult { // public static INSTANCE = new BreakSearchResult(); diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts index 4890907d5f9..7d141bfdad0 100644 --- a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts +++ b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts @@ -147,6 +147,13 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { 57, 'An his legere persecuti, oblique delicata efficiantur ex |vix, vel at graecis officiis maluisset. Et per impedit |voluptua, usu discere maiorum at. Ut assum ornatus |temporibus vis, an sea melius pericula. Ea dicunt |oblique phaedrum nam, eu duo movet nobis. His melius |facilis eu, vim malorum temporibus ne. Nec no sale |regione, meliore civibus placerat id eam. Mea alii |fabulas definitionem te, agam volutpat ad vis, et per |bonorum nonumes repudiandae.', 58, 'An his legere persecuti, oblique delicata efficiantur ex |vix, vel at graecis officiis maluisset. Et per impedit |voluptua, usu discere maiorum at. Ut assum ornatus |temporibus vis, an sea melius pericula. Ea dicunt oblique |phaedrum nam, eu duo movet nobis. His melius facilis eu, |vim malorum temporibus ne. Nec no sale regione, meliore |civibus placerat id eam. Mea alii fabulas definitionem te,| agam volutpat ad vis, et per bonorum nonumes repudiandae.' ); + + assertIncrementalLineMapping( + factory, '\t\t"owner": "vscode",', 4, + 14, '\t\t"owner|": |"vscod|e",', + 16, '\t\t"owner":| |"vscode"|,', + WrappingIndent.Same + ); }); From 5c02e4b2a149c1e2e9bb03b267d3b6727607cf72 Mon Sep 17 00:00:00 2001 From: Jason Fields Date: Thu, 9 Jan 2020 10:38:28 -0500 Subject: [PATCH 086/843] Remove extraneous line from doc comment in vscode.d.ts (#88336) --- src/vs/vscode.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 0ea85bb7df2..eabfc93a66b 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1193,7 +1193,6 @@ declare module 'vscode' { * A complex edit that will be applied in one transaction on a TextEditor. * This holds a description of the edits and if the edits are valid (i.e. no overlapping regions, document was not changed in the meantime, etc.) * they can be applied on a [document](#TextDocument) associated with a [text editor](#TextEditor). - * */ export interface TextEditorEdit { /** From b8a7184825f62443845c6850fc49a099bb5c6617 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 9 Jan 2020 17:36:45 +0100 Subject: [PATCH 087/843] use plugin --- .../src/modes/javascriptSemanticTokens.ts | 57 ++++++++++--------- .../typescript-language-features/package.json | 8 ++- ...{semanticColoring.ts => semanticTokens.ts} | 49 ++++++++++------ .../src/languageProvider.ts | 2 +- 4 files changed, 71 insertions(+), 45 deletions(-) rename extensions/typescript-language-features/src/features/{semanticColoring.ts => semanticTokens.ts} (81%) diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts index 7e4deafa262..9a1f394977a 100644 --- a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts +++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts @@ -55,37 +55,42 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current return resultTokens; } +enum TokenType { + 'class', + 'enum', + 'interface', + 'namespace', + 'typeParameter', + 'type', + 'parameter', + 'variable', + 'property', + 'constant', + 'function', + 'member', + _sentinel +} + + +enum TokenModifier { + 'declaration', + 'static', + 'async', + _sentinel +} export function getSemanticTokenLegend() { + const tokenTypes = []; + for (let i = 0; i < TokenType._sentinel; i++) { + tokenTypes.push(TokenType[i]); + } + const tokenModifiers = []; + for (let i = 0; i < TokenModifier._sentinel; i++) { + tokenModifiers.push(TokenModifier[i]); + } return { types: tokenTypes, modifiers: tokenModifiers }; } - -const tokenTypes: string[] = ['class', 'enum', 'interface', 'namespace', 'typeParameter', 'type', 'parameter', 'variable', 'property', 'constant', 'function', 'member']; -const tokenModifiers: string[] = ['declaration', 'static', 'async']; - -const enum TokenType { - 'class' = 0, - 'enum' = 1, - 'interface' = 2, - 'namespace' = 3, - 'typeParameter' = 4, - 'type' = 5, - 'parameter' = 6, - 'variable' = 7, - 'property' = 8, - 'constant' = 9, - 'function' = 10, - 'member' = 11 -} - - -const enum TokenModifier { - 'declaration' = 0x01, - 'static' = 0x02, - 'async' = 0x04, -} - const tokenFromDeclarationMapping: { [name: string]: TokenType } = { [ts.SyntaxKind.VariableDeclaration]: TokenType.variable, [ts.SyntaxKind.Parameter]: TokenType.parameter, diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 577a4fd1686..ce453e58d9a 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -20,7 +20,8 @@ "rimraf": "^2.6.3", "semver": "5.5.1", "vscode-extension-telemetry": "0.1.1", - "vscode-nls": "^4.0.0" + "vscode-nls": "^4.0.0", + "typescript-vscode-sh-plugin":"^0.1.0" }, "devDependencies": { "@types/node": "^12.11.7", @@ -953,6 +954,11 @@ } ] } + ], + "typescriptServerPlugins": [ + { + "name": "typescript-vscode-sh-plugin" + } ] } } diff --git a/extensions/typescript-language-features/src/features/semanticColoring.ts b/extensions/typescript-language-features/src/features/semanticTokens.ts similarity index 81% rename from extensions/typescript-language-features/src/features/semanticColoring.ts rename to extensions/typescript-language-features/src/features/semanticTokens.ts index 10a92c50bd2..cc9a8ba582a 100644 --- a/extensions/typescript-language-features/src/features/semanticColoring.ts +++ b/extensions/typescript-language-features/src/features/semanticTokens.ts @@ -56,14 +56,19 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider { return null; } + const versionBeforeRequest = document.version; + + if (_options.ranges) { + + // const allArgs = _options.ranges.map(r => ({file, start: document.offsetAt(r.start), length: document.offsetAt(r.end) - document.offsetAt(r.start)})); + } + const args: ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs = { file: file, start: 0, length: document.getText().length, }; - const versionBeforeRequest = document.version; - const response = await (this.client as ExperimentalProtocol.IExtendedTypeScriptServiceClient).execute('encodedSemanticClassifications-full', args, token); const versionAfterRequest = document.version; @@ -84,23 +89,33 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider { const tsTokens = response.body.spans; for (let i = 0, len = Math.floor(tsTokens.length / 3); i < len; i++) { - const tokenType = tokenTypeMap[tsTokens[3 * i + 2]]; - if (typeof tokenType === 'number') { - console.log(TokenType[tokenType]); - const offset = tsTokens[3 * i]; - const length = tsTokens[3 * i + 1]; - - // we can use the document's range conversion methods because - // the result is at the same version as the document - const startPos = document.positionAt(offset); - const endPos = document.positionAt(offset + length); - - for (let line = startPos.line; line <= endPos.line; line++) { - const startCharacter = (line === startPos.line ? startPos.character : 0); - const endCharacter = (line === endPos.line ? endPos.character : document.lineAt(line).text.length); - builder.push(line, startCharacter, endCharacter - startCharacter, tokenType, 0); + const tsClassification = tsTokens[3 * i + 2]; + let tokenType = 0; + let tokenModifiers = 0; + if (tsClassification > 0xFF) { + // classifications as returned by the typescript-vscode-sh-plugin + tokenType = (tsClassification >> 8) - 1; + tokenModifiers = tsClassification & 0xFF; + } else { + tokenType = tokenTypeMap[tsClassification]; + if (tokenType === undefined) { + continue; } } + + const offset = tsTokens[3 * i]; + const length = tsTokens[3 * i + 1]; + + // we can use the document's range conversion methods because + // the result is at the same version as the document + const startPos = document.positionAt(offset); + const endPos = document.positionAt(offset + length); + + for (let line = startPos.line; line <= endPos.line; line++) { + const startCharacter = (line === startPos.line ? startPos.character : 0); + const endCharacter = (line === endPos.line ? endPos.character : document.lineAt(line).text.length); + builder.push(line, startCharacter, endCharacter - startCharacter, tokenType, tokenModifiers); + } } return new vscode.SemanticTokens(builder.build()); diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 692cee35cbb..644eed40441 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -78,7 +78,7 @@ export default class LanguageProvider extends Disposable { import('./features/signatureHelp').then(provider => this._register(provider.register(selector, this.client))), import('./features/tagClosing').then(provider => this._register(provider.register(selector, this.description.id, this.client))), import('./features/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), - import('./features/semanticColoring').then(provider => this._register(provider.register(selector, this.client))), + import('./features/semanticTokens').then(provider => this._register(provider.register(selector, this.client))), import('./features/callHierarchy').then(provider => this._register(provider.register(selector, this.client))), ]); } From 5b5f01d72943396359555a25096e54677d7902a6 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 9 Jan 2020 17:41:17 +0100 Subject: [PATCH 088/843] :lipstick: --- .../characterHardWrappingLineMapper.ts | 466 ++++++++---------- 1 file changed, 212 insertions(+), 254 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 32b607c356e..91c4071798c 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -81,7 +81,12 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor finalize: () => { let result: (LineBreakingData | null)[] = []; for (let i = 0, len = requests.length; i < len; i++) { - result[i] = createLineMapping(this.classifier, previousBreakingData[i], requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + const previousLineBreakingData = previousBreakingData[i]; + if (previousLineBreakingData) { + result[i] = createLineMappingFromPreviousLineMapping(this.classifier, previousLineBreakingData, requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + } else { + result[i] = createLineMapping(this.classifier, requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + } } return result; } @@ -89,7 +94,7 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor } } -function createLineMapping(classifier: WrappingCharacterClassifier, previousBreakingData: LineBreakingData | null, lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { +function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterClassifier, previousBreakingData: LineBreakingData, lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { if (firstLineBreakingColumn === -1) { return null; } @@ -106,232 +111,243 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea let breakingOffsetsVisibleColumn: number[] = []; let breakingOffsetsCount: number = 0; - if (previousBreakingData/* && firstLineBreakingColumn >= 10 && Math.abs(previousBreakingData.breakingColumn - firstLineBreakingColumn) <= 3 */) { - const prevBreakingOffsets = previousBreakingData.breakOffsets; - const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakingOffsetsVisibleColumn; + const prevBreakingOffsets = previousBreakingData.breakOffsets; + const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakingOffsetsVisibleColumn; - let breakingColumn = firstLineBreakingColumn; - let lastBreakingOffsetVisibleColumn = 0; - const prevLen = prevBreakingOffsets.length; - let prevIndex = 0; + let breakingColumn = firstLineBreakingColumn; + const prevLen = prevBreakingOffsets.length; + let prevIndex = 0; - if (prevIndex >= 0) { - let currentDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); - while (prevIndex + 1 < prevLen) { - const potentialDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); - if (potentialDiff >= currentDiff) { + if (prevIndex >= 0) { + let currentDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); + while (prevIndex + 1 < prevLen) { + const potentialDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); + if (potentialDiff >= currentDiff) { + break; + } + currentDiff = potentialDiff; + prevIndex++; + } + } + + while (prevIndex < prevLen) { + // Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break) + const prevBreakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex]; + const prevBreakoffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex];; + + let breakOffset = 0; + let breakOffsetVisibleColumn = 0; + + let forcedBreakOffset = 0; + let forcedBreakOffsetVisibleColumn = 0; + + // initially, we search as much as possible to the right (if it fits) + if (prevBreakoffsetVisibleColumn <= breakingColumn) { + let visibleColumn = prevBreakoffsetVisibleColumn; + let prevCharCode = lineText.charCodeAt(prevBreakOffset - 1); + let prevCharCodeClass = classifier.get(prevCharCode); + let entireLineFits = true; + for (let i = prevBreakOffset; i < len; i++) { + const charCode = lineText.charCodeAt(i); + const charCodeClass = classifier.get(charCode); + + if (strings.isHighSurrogate(prevCharCode)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + visibleColumn += 1; + prevCharCode = charCode; + prevCharCodeClass = charCodeClass; + continue; + + } + + if (canBreak(prevCharCodeClass, charCodeClass)) { + breakOffset = i; + breakOffsetVisibleColumn = visibleColumn; + } + + const charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); + visibleColumn += charWidth; + + if (visibleColumn > breakingColumn) { + // We need to break at least before character at `i`: + forcedBreakOffset = i; + forcedBreakOffsetVisibleColumn = visibleColumn - charWidth; + + if (visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { + // Cannot break at `breakOffset` => reset it if it was set + breakOffset = 0; + } + + entireLineFits = false; break; } - currentDiff = potentialDiff; - prevIndex++; + + prevCharCode = charCode; + prevCharCodeClass = charCodeClass; + } + + if (entireLineFits) { + // there is no more need to break => stop the outer loop! + // Add last segment + breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1]; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1]; + break; } } - while (prevIndex < prevLen) { - // Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break) - const prevBreakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex]; - const prevBreakoffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex];; + if (breakOffset === 0) { + // must search left + let visibleColumn = prevBreakoffsetVisibleColumn; + let charCode = lineText.charCodeAt(prevBreakOffset); + let charCodeClass = classifier.get(charCode); + let hitATabCharacter = false; + for (let i = prevBreakOffset - 1; i >= 0; i--) { + const prevCharCode = lineText.charCodeAt(i); + const prevCharCodeClass = classifier.get(prevCharCode); - let breakOffset = 0; - let breakOffsetVisibleColumn = 0; + if (strings.isHighSurrogate(prevCharCode)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + visibleColumn -= 1; + charCode = prevCharCode; + charCodeClass = prevCharCodeClass; + continue; + } - let forcedBreakOffset = 0; - let forcedBreakOffsetVisibleColumn = 0; + if (prevCharCode === CharCode.Tab) { + // cannot determine the width of a tab when going backwards, so we must go forwards + hitATabCharacter = true; + break; + } - // initially, we search as much as possible to the right (if it fits) - if (prevBreakoffsetVisibleColumn <= breakingColumn) { - let visibleColumn = prevBreakoffsetVisibleColumn; - let prevCharCode = lineText.charCodeAt(prevBreakOffset - 1); - let prevCharCodeClass = classifier.get(prevCharCode); - let entireLineFits = true; - for (let i = prevBreakOffset; i < len; i++) { - const charCode = lineText.charCodeAt(i); - const charCodeClass = classifier.get(charCode); + const charWidth = (strings.isFullWidthCharacter(prevCharCode) ? columnsForFullWidthChar : 1); - if (strings.isHighSurrogate(prevCharCode)) { - // A surrogate pair must always be considered as a single unit, so it is never to be broken - visibleColumn += 1; - prevCharCode = charCode; - prevCharCodeClass = charCodeClass; - continue; + if (visibleColumn <= breakingColumn) { + if (forcedBreakOffset === 0) { + forcedBreakOffset = i + 1; + forcedBreakOffsetVisibleColumn = visibleColumn; + } + if (visibleColumn <= breakingColumn - wrappedLineBreakingColumn) { + // went too far! + break; } if (canBreak(prevCharCodeClass, charCodeClass)) { - breakOffset = i; + breakOffset = i + 1; breakOffsetVisibleColumn = visibleColumn; - } - - const charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); - visibleColumn += charWidth; - - if (visibleColumn > breakingColumn) { - // We need to break at least before character at `i`: - forcedBreakOffset = i; - forcedBreakOffsetVisibleColumn = visibleColumn - charWidth; - - if (visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { - // Cannot break at `breakOffset` => reset it if it was set - breakOffset = 0; - } - - entireLineFits = false; break; } - - prevCharCode = charCode; - prevCharCodeClass = charCodeClass; } - if (entireLineFits) { - // there is no more need to break => stop the outer loop! - // Add last segment - breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1]; - breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1]; - break; - } + visibleColumn -= charWidth; + charCode = prevCharCode; + charCodeClass = prevCharCodeClass; } - if (breakOffset === 0) { - // must search left - let visibleColumn = prevBreakoffsetVisibleColumn; - let charCode = lineText.charCodeAt(prevBreakOffset); - let charCodeClass = classifier.get(charCode); - let hitATabCharacter = false; - for (let i = prevBreakOffset - 1; i >= 0; i--) { - const prevCharCode = lineText.charCodeAt(i); - const prevCharCodeClass = classifier.get(prevCharCode); - - if (strings.isHighSurrogate(prevCharCode)) { - // A surrogate pair must always be considered as a single unit, so it is never to be broken - visibleColumn -= 1; - charCode = prevCharCode; - charCodeClass = prevCharCodeClass; - continue; - } - - if (prevCharCode === CharCode.Tab) { - // cannot determine the width of a tab when going backwards, so we must go forwards - hitATabCharacter = true; - break; - } - - const charWidth = (strings.isFullWidthCharacter(prevCharCode) ? columnsForFullWidthChar : 1); - - if (visibleColumn <= breakingColumn) { - if (forcedBreakOffset === 0) { - forcedBreakOffset = i + 1; - forcedBreakOffsetVisibleColumn = visibleColumn; - } - - if (visibleColumn <= breakingColumn - wrappedLineBreakingColumn) { - // went too far! - break; - } - - if (canBreak(prevCharCodeClass, charCodeClass)) { - breakOffset = i + 1; - breakOffsetVisibleColumn = visibleColumn; - break; - } - } - - visibleColumn -= charWidth; - charCode = prevCharCode; - charCodeClass = prevCharCodeClass; - } - - if (hitATabCharacter) { - // cannot determine the width of a tab when going backwards, so we must go forwards from the previous break - prevIndex--; - continue; - } - } - - if (breakOffset === 0) { - // Could not find a good breaking point - breakOffset = forcedBreakOffset; - breakOffsetVisibleColumn = forcedBreakOffsetVisibleColumn; - } - - breakingOffsets[breakingOffsetsCount] = breakOffset; - breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; - breakingOffsetsCount++; - breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; - - while (prevIndex < 0 || (prevIndex < prevLen && prevBreakingOffsetsVisibleColumn[prevIndex] < breakOffsetVisibleColumn)) { - prevIndex++; - } - - let currentDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); - // let lastBreakingColumn - while (prevIndex + 1 < prevLen) { - const potentialDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); - if (potentialDiff >= currentDiff) { - break; - } - currentDiff = potentialDiff; - prevIndex++; + if (hitATabCharacter) { + // cannot determine the width of a tab when going backwards, so we must go forwards from the previous break + prevIndex--; + continue; } } - if (breakingOffsetsCount === 0) { - return null; + if (breakOffset === 0) { + // Could not find a good breaking point + breakOffset = forcedBreakOffset; + breakOffsetVisibleColumn = forcedBreakOffsetVisibleColumn; } - return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); - const expected = createLineMapping(classifier, null, lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); - const actual = new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); - try { - actual.assertEqual(expected); - } catch (err) { - console.log(`BREAKING!!`); - console.log(err); - console.log(`previous breaks: ${JSON.stringify(prevBreakingOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(prevBreakingOffsetsVisibleColumn)}`); - console.log(`expected breakOffsets: ${JSON.stringify(expected?.breakOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(expected?.breakingOffsetsVisibleColumn)}`); - console.log(`actual breakOffsets: ${JSON.stringify(actual?.breakOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(actual?.breakingOffsetsVisibleColumn)}`); + breakingOffsets[breakingOffsetsCount] = breakOffset; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; + breakingOffsetsCount++; + breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; - console.log(`actual str: ${toAnnotatedText(lineText, actual)}`); - - - console.log(` - assertIncrementalLineMapping( - factory, ${str(lineText)}, 4, - ${previousBreakingData.breakingColumn}, ${str(toAnnotatedText(lineText, previousBreakingData))}, - ${expected!.breakingColumn}, ${str(toAnnotatedText(lineText, expected))}, - WrappingIndent.${hardWrappingIndent === WrappingIndent.None ? 'None' : hardWrappingIndent === WrappingIndent.Same ? 'Same' : hardWrappingIndent === WrappingIndent.Indent ? 'Indent' : 'DeepIndent'} - ); -`); - function str(strr: string) { - return `'${strr.replace(/'/g, '\\\'')}'`; - } - function toAnnotatedText(text: string, lineBreakingData: LineBreakingData | null): string { - // Insert line break markers again, according to algorithm - let actualAnnotatedText = ''; - if (lineBreakingData) { - let previousLineIndex = 0; - for (let i = 0, len = text.length; i < len; i++) { - let r = LineBreakingData.getOutputPositionOfInputOffset(lineBreakingData.breakOffsets, i); - if (previousLineIndex !== r.outputLineIndex) { - previousLineIndex = r.outputLineIndex; - actualAnnotatedText += '|'; - } - actualAnnotatedText += text.charAt(i); - } - } else { - // No wrapping - actualAnnotatedText = text; - } - return actualAnnotatedText; - } + while (prevIndex < 0 || (prevIndex < prevLen && prevBreakingOffsetsVisibleColumn[prevIndex] < breakOffsetVisibleColumn)) { + prevIndex++; } - return actual; - breakingOffsets = []; - breakingOffsetsVisibleColumn = []; - breakingOffsetsCount = 0; + let currentDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); + // let lastBreakingColumn + while (prevIndex + 1 < prevLen) { + const potentialDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); + if (potentialDiff >= currentDiff) { + break; + } + currentDiff = potentialDiff; + prevIndex++; + } } + if (breakingOffsetsCount === 0) { + return null; + } + + // return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); + const expected = createLineMapping(classifier, lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); + const actual = new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); + try { + actual.assertEqual(expected); + } catch (err) { + console.log(`BREAKING!!`); + console.log(err); + console.log(`previous breaks: ${JSON.stringify(prevBreakingOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(prevBreakingOffsetsVisibleColumn)}`); + console.log(`expected breakOffsets: ${JSON.stringify(expected?.breakOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(expected?.breakingOffsetsVisibleColumn)}`); + console.log(`actual breakOffsets: ${JSON.stringify(actual?.breakOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(actual?.breakingOffsetsVisibleColumn)}`); + + console.log(`actual str: ${toAnnotatedText(lineText, actual)}`); + + + console.log(` + assertIncrementalLineMapping( + factory, ${str(lineText)}, 4, + ${previousBreakingData.breakingColumn}, ${str(toAnnotatedText(lineText, previousBreakingData))}, + ${expected!.breakingColumn}, ${str(toAnnotatedText(lineText, expected))}, + WrappingIndent.${hardWrappingIndent === WrappingIndent.None ? 'None' : hardWrappingIndent === WrappingIndent.Same ? 'Same' : hardWrappingIndent === WrappingIndent.Indent ? 'Indent' : 'DeepIndent'} + ); +`); + function str(strr: string) { + return `'${strr.replace(/'/g, '\\\'')}'`; + } + function toAnnotatedText(text: string, lineBreakingData: LineBreakingData | null): string { + // Insert line break markers again, according to algorithm + let actualAnnotatedText = ''; + if (lineBreakingData) { + let previousLineIndex = 0; + for (let i = 0, len = text.length; i < len; i++) { + let r = LineBreakingData.getOutputPositionOfInputOffset(lineBreakingData.breakOffsets, i); + if (previousLineIndex !== r.outputLineIndex) { + previousLineIndex = r.outputLineIndex; + actualAnnotatedText += '|'; + } + actualAnnotatedText += text.charAt(i); + } + } else { + // No wrapping + actualAnnotatedText = text; + } + return actualAnnotatedText; + } + } + return actual; + +} + +function createLineMapping(classifier: WrappingCharacterClassifier, lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { + if (firstLineBreakingColumn === -1) { + return null; + } + + const len = lineText.length; + if (len <= 1) { + return null; + } + + const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); + const wrappedLineBreakingColumn = firstLineBreakingColumn - wrappedTextIndentLength; + + let breakingOffsets: number[] = []; + let breakingOffsetsVisibleColumn: number[] = []; + let breakingOffsetsCount: number = 0; let breakOffset = 0; let breakOffsetVisibleColumn = 0; @@ -392,64 +408,6 @@ function createLineMapping(classifier: WrappingCharacterClassifier, previousBrea return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); } -function - -// class BreakSearchResult { - -// public static INSTANCE = new BreakSearchResult(); - -// prevCharCode: number = CharCode.Null; -// prevCharCodeClass: CharacterClass = CharacterClass.NONE; -// breakOffset: number = 0; -// breakOffsetVisibleColumn: number = 0; -// visibleColumn: number = 0; -// } - -// function searchForBreak(classifier: WrappingCharacterClassifier, lineText: string, len: number, prevCharCode: number, prevCharCodeClass: number): boolean { -// let breakOffset = 0; -// let breakOffsetVisibleColumn = 0; -// for (let i = 1; i < len; i++) { -// const charCode = lineText.charCodeAt(i); -// const charCodeClass = classifier.get(charCode); - -// if (strings.isHighSurrogate(prevCharCode)) { -// // A surrogate pair must always be considered as a single unit, so it is never to be broken -// visibleColumn += 1; -// prevCharCode = charCode; -// prevCharCodeClass = charCodeClass; -// continue; -// } - -// if (canBreak(prevCharCodeClass, charCodeClass)) { -// breakOffset = i; -// breakOffsetVisibleColumn = visibleColumn; -// } - -// const charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); -// visibleColumn += charWidth; - -// // check if adding character at `i` will go over the breaking column -// if (visibleColumn > breakingColumn) { -// // We need to break at least before character at `i`: - -// if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { -// // Cannot break at `breakOffset`, must break at `i` -// breakOffset = i; -// breakOffsetVisibleColumn = visibleColumn - charWidth; -// } - -// breakingOffsets[breakingOffsetsCount] = breakOffset; -// breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; -// breakingOffsetsCount++; -// breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; -// breakOffset = 0; -// } - -// prevCharCode = charCode; -// prevCharCodeClass = charCodeClass; -// } -// } - function computeCharWidth(charCode: number, visibleColumn: number, tabSize: number, columnsForFullWidthChar: number): number { if (charCode === CharCode.Tab) { return (tabSize - (visibleColumn % tabSize)); From a07286f7f9f0d0810cad80ca3314061c39079452 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 9 Jan 2020 22:47:06 +0100 Subject: [PATCH 089/843] use @aeschli/typescript-vscode-sh-plugin --- .../typescript-language-features/package.json | 2 +- .../typescript-language-features/yarn.lock | 1748 +++-------------- 2 files changed, 265 insertions(+), 1485 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index ce453e58d9a..3191e9fc274 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -21,7 +21,7 @@ "semver": "5.5.1", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0", - "typescript-vscode-sh-plugin":"^0.1.0" + "@aeschli/typescript-vscode-sh-plugin": "0.2.0" }, "devDependencies": { "@types/node": "^12.11.7", diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index a45e26290c6..054c7c6f146 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@aeschli/typescript-vscode-sh-plugin@0.2.0": + version "0.2.0" + resolved "https://npm.pkg.github.com/download/@aeschli/typescript-vscode-sh-plugin/0.2.0/6e1ccd1d9ed6c3251091de08bb83589bc555f203b1c594fc9c2bc9d38b66b44d#" + integrity sha512-QHORCwbuln5T9gdfDoqJ2nNXzY/8yOBgTG2f+GiQPeQ0rGrNIydbKe0IYi7VmCvIm1LGhEcNOihcF4FWmsKFow== + "@types/events@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -22,14 +27,14 @@ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.2.tgz#3452a24edf9fea138b48fad4a0a028a683da1e40" - integrity sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA== + version "13.1.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.1.6.tgz#076028d0b0400be8105b89a0a55550c86684ffec" + integrity "sha1-B2Ao0LBAC+gQW4mgpVVQyGaE/+w= sha512-Jg1F+bmxcpENHP23sVKkNuU3uaxPnsBMW0cLjleiikFKomJQbsn0Cqk2yDvQArqzZN6ABfBkZ0To7pQ8sLdWDg==" "@types/node@^12.11.7": - version "12.11.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.7.tgz#57682a9771a3f7b09c2497f28129a0462966524a" - integrity sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA== + version "12.12.24" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.24.tgz#d4606afd8cf6c609036b854360367d1b2c78931f" + integrity "sha1-1GBq/Yz2xgkDa4VDYDZ9Gyx4kx8= sha512-1Ciqv9pqwVtW6FsIUKSZNB82E5Cu1I2bBTj1xuIHXLe/1zYLl3956Nbhg2MzSYHVfl9/rmanjbQIb7LibfCnug==" "@types/rimraf@2.0.2": version "2.0.2" @@ -44,51 +49,22 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ== -ajv@^5.1.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= +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: - co "^4.6.0" - fast-deep-equal "^1.0.0" + 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.3.0" - -ansi-cyan@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" - integrity sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM= - dependencies: - ansi-wrap "0.1.0" - -ansi-gray@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" - integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= - dependencies: - ansi-wrap "0.1.0" - -ansi-red@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c" - integrity sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw= - dependencies: - ansi-wrap "0.1.0" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-wrap@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" - integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" applicationinsights@1.0.8: version "1.0.8" @@ -99,67 +75,12 @@ applicationinsights@1.0.8: diagnostic-channel-publishers "0.2.1" zone.js "0.7.6" -arr-diff@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a" - integrity sha1-aHwydYFjWI/vfeezb6vklesaOZo= - dependencies: - arr-flatten "^1.0.1" - array-slice "^0.2.3" - -arr-diff@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" - integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= - dependencies: - arr-flatten "^1.0.1" - -arr-flatten@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-2.1.0.tgz#20f9eab5ec70f5c7d215b1077b1c39161d292c7d" - integrity sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0= - -array-differ@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" - integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE= - -array-slice@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" - integrity sha1-3Tz7gO15c6dRF82sabC5nshhhvU= - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1, array-uniq@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= - -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= - -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= - asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" - integrity sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y= + 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" @@ -176,10 +97,10 @@ aws-sign2@~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.6.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" - integrity sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w== +aws4@^1.8.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.0.tgz#24390e6ad61386b0a747265754d2a17219de862c" + integrity "sha1-JDkOatYThrCnRyZXVNKhchnehiw= sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==" balanced-match@^1.0.0: version "1.0.0" @@ -187,38 +108,12 @@ balanced-match@^1.0.0: integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= bcrypt-pbkdf@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" - integrity sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40= + 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" -beeper@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" - integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak= - -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= - dependencies: - inherits "~2.0.0" - -boom@4.x.x: - version "4.3.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" - integrity sha1-T4owBctKfjiJ90kDD9JbluAdLjE= - dependencies: - hoek "4.x.x" - -boom@5.x.x: - version "5.2.0" - resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" - integrity sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw== - dependencies: - hoek "4.x.x" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -227,129 +122,43 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - -browser-stdout@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" - integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8= - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= +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.0.0" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531" - integrity sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA== + 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= -chalk@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= - -clone-stats@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" - integrity sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE= - -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= - -clone@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" - integrity sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8= - -clone@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= - -clone@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" - integrity sha1-0hfR6WERjjrJpLi7oyhVU79kfNs= - -cloneable-readable@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.2.tgz#d591dee4a8f8bc15da43ce97dceeba13d43e2a65" - integrity sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg== - dependencies: - inherits "^2.0.1" - process-nextick-args "^2.0.0" - readable-stream "^2.3.5" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - -combined-stream@1.0.6, combined-stream@~1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" - integrity sha1-cj599ugBrFYTETp+RFqbactjKBg= +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.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" - integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ== +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= -convert-source-map@^1.1.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" - integrity sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU= - -core-util-is@1.0.2, core-util-is@~1.0.0: +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= -cryptiles@3.x.x: - version "3.1.2" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" - integrity sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4= - dependencies: - boom "5.x.x" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -357,11 +166,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -dateformat@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" - integrity sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI= - debug@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -369,12 +173,12 @@ debug@3.1.0: dependencies: ms "2.0.0" -deep-assign@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/deep-assign/-/deep-assign-1.0.0.tgz#b092743be8427dc621ea0067cdec7e70dd19f37b" - integrity sha1-sJJ0O+hCfcYh6gBnzex+cN0Z83s= +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: - is-obj "^1.0.0" + ms "^2.1.1" delayed-stream@~1.0.0: version "1.0.0" @@ -393,104 +197,40 @@ diagnostic-channel@0.2.0: dependencies: semver "^5.3.0" -diff@3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.1.tgz#aa8567a6eed03c531fc89d3f711cd0e5259dec75" - integrity sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww== - -duplexer2@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" - integrity sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds= - dependencies: - readable-stream "~1.1.9" - -duplexer@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" - integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= - -duplexify@^3.2.0: - version "3.5.4" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.4.tgz#4bb46c1796eabebeec4ca9a2e66b808cb7a3d8b4" - integrity sha512-JzYSLYMhoVVBe8+mbHQ4KgpvHpm0DZpJuL8PY93Vyv1fW7jYJ90LoXa1di/CVbJM+TgMs91rbDapE/RNIfnJsA== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" +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.1" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" - integrity sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU= + 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" -end-of-stream@^1.0.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" - integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== +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: - once "^1.4.0" + es6-promise "^4.0.3" -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2: +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= -event-stream@^3.3.1, event-stream@^3.3.4, event-stream@~3.3.4: - version "3.3.4" - resolved "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" - integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= - dependencies: - duplexer "~0.1.1" - from "~0" - map-stream "~0.1.0" - pause-stream "0.0.11" - split "0.3" - stream-combiner "~0.0.4" - through "~2.3.1" - -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= - dependencies: - is-posix-bracket "^0.1.0" - -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= - dependencies: - fill-range "^2.1.0" - -extend-shallow@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-1.1.4.tgz#19d6bf94dfc09d76ba711f39b872d21ff4dd9071" - integrity sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE= - dependencies: - kind-of "^1.1.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend@^3.0.0, extend@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" - integrity sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ= - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= - dependencies: - is-extglob "^1.0.0" +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" @@ -502,99 +242,35 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fancy-log@^1.1.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.2.tgz#f41125e3d84f2e7d89a43d06d958c8f78be16be1" - integrity sha1-9BEl49hPLn2JpD0G2VjI94vha+E= - dependencies: - ansi-gray "^0.1.1" - color-support "^1.1.3" - time-stamp "^1.0.0" - -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" - integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= +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= - -fd-slicer@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" - integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= - dependencies: - pend "~1.2.0" - -filename-regex@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" - integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= - -fill-range@^2.1.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" - integrity sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM= - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^1.1.3" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - -first-chunk-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" - integrity sha1-Wb+1DNkF9g18OUzT2ayqtOatk04= - -for-in@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -for-own@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= - dependencies: - for-in "^1.0.1" + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM= sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" 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.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" - integrity sha1-SXBJi+YEwgwAXU9cI67NIda0kJk= +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" + combined-stream "^1.0.6" mime-types "^2.1.12" -from@~0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" - integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= - 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= -fstream@^1.0.2: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE= - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -602,44 +278,7 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= - dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" - -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= - dependencies: - is-glob "^2.0.0" - -glob-parent@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob-stream@^5.3.2: - version "5.3.5" - resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-5.3.5.tgz#a55665a9a8ccdc41915a87c701e32d4e016fad22" - integrity sha1-pVZlqajM3EGRWofHAeMtTgFvrSI= - dependencies: - extend "^3.0.0" - glob "^5.0.3" - glob-parent "^3.0.0" - micromatch "^2.3.7" - ordered-read-streams "^0.3.0" - through2 "^0.6.0" - to-absolute-glob "^0.1.1" - unique-stream "^2.0.2" - -glob@7.1.2, glob@^7.0.5, glob@^7.1.2: +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== @@ -651,21 +290,10 @@ glob@7.1.2, glob@^7.0.5, glob@^7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^5.0.3: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.1.3: - version "7.1.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" - integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== +glob@^7.1.2, glob@^7.1.3: + 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" @@ -674,187 +302,41 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glogg@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.1.tgz#dcf758e44789cc3f3d32c1f3562a3676e6a34810" - integrity sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw== - dependencies: - sparkles "^1.0.0" - -graceful-fs@^4.0.0, graceful-fs@^4.1.2: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= - -growl@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.3.tgz#1926ba90cf3edfe2adb4927f5880bc22c66c790f" - integrity sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q== - -gulp-chmod@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/gulp-chmod/-/gulp-chmod-2.0.0.tgz#00c390b928a0799b251accf631aa09e01cc6299c" - integrity sha1-AMOQuSigeZslGsz2MaoJ4BzGKZw= - dependencies: - deep-assign "^1.0.0" - stat-mode "^0.2.0" - through2 "^2.0.0" - -gulp-filter@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/gulp-filter/-/gulp-filter-5.1.0.tgz#a05e11affb07cf7dcf41a7de1cb7b63ac3783e73" - integrity sha1-oF4Rr/sHz33PQafeHLe2OsN4PnM= - dependencies: - multimatch "^2.0.0" - plugin-error "^0.1.2" - streamfilter "^1.0.5" - -gulp-gunzip@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulp-gunzip/-/gulp-gunzip-1.0.0.tgz#15b741145e83a9c6f50886241b57cc5871f151a9" - integrity sha1-FbdBFF6Dqcb1CIYkG1fMWHHxUak= - dependencies: - through2 "~0.6.5" - vinyl "~0.4.6" - -gulp-remote-src-vscode@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/gulp-remote-src-vscode/-/gulp-remote-src-vscode-0.5.0.tgz#71785553bc491880088ad971f90910c4b2d80a99" - integrity sha512-/9vtSk9eI9DEWCqzGieglPqmx0WUQ9pwPHyHFpKmfxqdgqGJC2l0vFMdYs54hLdDsMDEZFLDL2J4ikjc4hQ5HQ== - dependencies: - event-stream "^3.3.4" - node.extend "^1.1.2" - request "^2.79.0" - through2 "^2.0.3" - vinyl "^2.0.1" - -gulp-sourcemaps@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz#b86ff349d801ceb56e1d9e7dc7bbcb4b7dee600c" - integrity sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw= - dependencies: - convert-source-map "^1.1.1" - graceful-fs "^4.1.2" - strip-bom "^2.0.0" - through2 "^2.0.0" - vinyl "^1.0.0" - -gulp-symdest@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/gulp-symdest/-/gulp-symdest-1.1.0.tgz#c165320732d192ce56fd94271ffa123234bf2ae0" - integrity sha1-wWUyBzLRks5W/ZQnH/oSMjS/KuA= - dependencies: - event-stream "^3.3.1" - mkdirp "^0.5.1" - queue "^3.1.0" - vinyl-fs "^2.4.3" - -gulp-untar@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/gulp-untar/-/gulp-untar-0.0.6.tgz#d6bdefde7e9a8e054c9f162385a0782c4be74000" - integrity sha1-1r3v3n6ajgVMnxYjhaB4LEvnQAA= - dependencies: - event-stream "~3.3.4" - gulp-util "~3.0.8" - streamifier "~0.1.1" - tar "^2.2.1" - through2 "~2.0.3" - -gulp-util@~3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" - integrity sha1-AFTh50RQLifATBh8PsxQXdVLu08= - dependencies: - array-differ "^1.0.0" - array-uniq "^1.0.2" - beeper "^1.0.0" - chalk "^1.0.0" - dateformat "^2.0.0" - fancy-log "^1.1.0" - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash._reescape "^3.0.0" - lodash._reevaluate "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.template "^3.0.0" - minimist "^1.1.0" - multipipe "^0.1.2" - object-assign "^3.0.0" - replace-ext "0.0.1" - through2 "^2.0.0" - vinyl "^0.5.0" - -gulp-vinyl-zip@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/gulp-vinyl-zip/-/gulp-vinyl-zip-2.1.0.tgz#24e40685dc05b7149995245099e0590263be8dad" - integrity sha1-JOQGhdwFtxSZlSRQmeBZAmO+ja0= - dependencies: - event-stream "^3.3.1" - queue "^4.2.1" - through2 "^2.0.3" - vinyl "^2.0.2" - vinyl-fs "^2.0.0" - yauzl "^2.2.1" - yazl "^2.2.1" - -gulplog@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" - integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= - dependencies: - glogg "^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.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" - integrity sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0= +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 "^5.1.0" + ajv "^6.5.5" har-schema "^2.0.0" -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" - integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= - -has-gulplog@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" - integrity sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4= - dependencies: - sparkles "^1.0.0" - -hawk@~6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" - integrity sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ== - dependencies: - boom "4.x.x" - cryptiles "3.x.x" - hoek "4.x.x" - sntp "2.x.x" +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= -hoek@4.x.x: - version "4.2.1" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" - integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA== +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" @@ -865,6 +347,14 @@ http-signature@~1.2.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" @@ -873,128 +363,16 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-dotfile@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= - -is-equal-shallow@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= - dependencies: - is-primitive "^2.0.0" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= - -is-extglob@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-glob@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= - dependencies: - is-extglob "^1.0.0" - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - -is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= - dependencies: - kind-of "^3.0.2" - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - -is-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= - -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= - -is-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= - -is-stream@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +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= -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - -is-valid-glob@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-0.3.0.tgz#d4b55c69f51886f9b65c70d6c2622d37e29f48fe" - integrity sha1-1LVcafUYhvm2XHDWwmItN+KfSP4= - -is@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/is/-/is-3.2.1.tgz#d0ac2ad55eb7b0bec926a5266f6c662aaa83dca5" - integrity sha1-0Kwq1V63sL7JJqUmb2xmKqqD3KU= - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -1005,37 +383,25 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= +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-stable-stringify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= - dependencies: - jsonify "~0.0.0" - 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= jsonc-parser@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e" - integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g== - -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + version "2.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.0.tgz#f206f87f9d49d644b7502052c04e82dd6392e9ef" + integrity sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA== jsprim@^1.2.2: version "1.4.1" @@ -1047,180 +413,19 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -kind-of@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44" - integrity sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ= +mime-db@1.43.0: + version "1.43.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" + integrity "sha1-ChLgUCZQ5HPXNVNQUOfI9OtPrlg= sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" -kind-of@^3.0.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.26" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" + integrity "sha1-nJIfwJt+FJpl39wNpNIJlyALCgY= sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==" dependencies: - is-buffer "^1.1.5" + mime-db "1.43.0" -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -lazystream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" - integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= - dependencies: - readable-stream "^2.0.5" - -lodash._basecopy@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" - integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= - -lodash._basetostring@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" - integrity sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U= - -lodash._basevalues@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" - integrity sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc= - -lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= - -lodash._isiterateecall@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" - integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= - -lodash._reescape@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" - integrity sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo= - -lodash._reevaluate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" - integrity sha1-WLx0xAZklTrgsSTYBpltrKQx4u0= - -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= - -lodash._root@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" - integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= - -lodash.escape@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" - integrity sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg= - dependencies: - lodash._root "^3.0.0" - -lodash.isarguments@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" - integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= - -lodash.isarray@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" - integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= - -lodash.isequal@^4.0.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= - -lodash.keys@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" - integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= - dependencies: - lodash._getnative "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - -lodash.restparam@^3.0.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= - -lodash.template@^3.0.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" - integrity sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8= - dependencies: - lodash._basecopy "^3.0.0" - lodash._basetostring "^3.0.0" - lodash._basevalues "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - lodash.keys "^3.0.0" - lodash.restparam "^3.0.0" - lodash.templatesettings "^3.0.0" - -lodash.templatesettings@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" - integrity sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU= - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - -map-stream@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" - integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= - -merge-stream@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" - integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE= - dependencies: - readable-stream "^2.0.1" - -micromatch@^2.3.7: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" - -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== - -mime-types@^2.1.12, mime-types@~2.1.17: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== - dependencies: - mime-db "~1.33.0" - -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.4: +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== @@ -1232,319 +437,134 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - -mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: +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@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-4.1.0.tgz#7d86cfbcf35cb829e2754c32e17355ec05338794" - integrity sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA== +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.0" - commander "2.11.0" + browser-stdout "1.3.1" + commander "2.15.1" debug "3.1.0" - diff "3.3.1" + diff "3.5.0" escape-string-regexp "1.0.5" glob "7.1.2" - growl "1.10.3" + growl "1.10.5" he "1.1.1" + minimatch "3.0.4" mkdirp "0.5.1" - supports-color "4.4.0" + 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= -multimatch@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" - integrity sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis= - dependencies: - array-differ "^1.0.0" - array-union "^1.0.1" - arrify "^1.0.0" - minimatch "^3.0.0" +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== -multipipe@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" - integrity sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s= - dependencies: - duplexer2 "0.0.2" +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== -node.extend@^1.1.2: - version "1.1.6" - resolved "https://registry.yarnpkg.com/node.extend/-/node.extend-1.1.6.tgz#a7b882c82d6c93a4863a5504bd5de8ec86258b96" - integrity sha1-p7iCyC1sk6SGOlUEvV3o7IYli5Y= - dependencies: - is "^3.1.0" - -normalize-path@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - -oauth-sign@~0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" - integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM= - -object-assign@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" - integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= - -object-assign@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object.omit@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" - integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= - dependencies: - for-own "^0.1.4" - is-extendable "^0.1.1" - -once@^1.3.0, once@^1.4.0: +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" -ordered-read-streams@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz#7137e69b3298bb342247a1bbee3881c80e2fd78b" - integrity sha1-cTfmmzKYuzQiR6G77jiByA4v14s= - dependencies: - is-stream "^1.0.1" - readable-stream "^2.0.1" - -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - 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= -pause-stream@0.0.11: - version "0.0.11" - resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" - integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= - dependencies: - through "~2.3" - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - 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= -plugin-error@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace" - integrity sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4= - dependencies: - ansi-cyan "^0.1.1" - ansi-red "^0.1.1" - arr-diff "^1.0.1" - arr-union "^2.0.1" - extend-shallow "^1.1.2" - -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= - -process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== +psl@^1.1.24: + version "1.7.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" + integrity "sha1-8cTEeo75cWfepda79IFtc26ISjw= sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -qs@~6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" - integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== +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== -querystringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.0.0.tgz#fa3ed6e68eb15159457c89b37bc6472833195755" - integrity sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw== +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== -queue@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/queue/-/queue-3.1.0.tgz#6c49d01f009e2256788789f2bffac6b8b9990585" - integrity sha1-bEnQHwCeIlZ4h4nyv/rGuLmZBYU= - dependencies: - inherits "~2.0.0" +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== -queue@^4.2.1: - version "4.4.2" - resolved "https://registry.yarnpkg.com/queue/-/queue-4.4.2.tgz#5a9733d9a8b8bd1b36e934bc9c55ab89b28e29c7" - integrity sha512-fSMRXbwhMwipcDZ08enW2vl+YDmAmhcNcr43sCJL8DIg+CFOsoRLG23ctxA+fwNk1w55SePSiS7oqQQSgQoVJQ== - dependencies: - inherits "~2.0.0" - -randomatic@^1.1.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" - integrity sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how== - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -"readable-stream@>=1.0.33-1 <1.1.0-0": - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -regex-cache@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" - integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== - dependencies: - is-equal-shallow "^0.1.3" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -repeat-element@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" - integrity sha1-7wiaF40Ug7quTZPrmLT55OEdmQo= - -repeat-string@^1.5.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -replace-ext@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" - integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= - -replace-ext@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" - integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= - -request@^2.79.0, request@^2.83.0: - version "2.85.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" - integrity sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg== +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.6.0" + aws4 "^1.8.0" caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" + combined-stream "~1.0.6" + extend "~3.0.2" forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" - hawk "~6.0.2" + 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.17" - oauth-sign "~0.8.2" + mime-types "~2.1.19" + oauth-sign "~0.9.0" performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - stringstream "~0.0.5" - tough-cookie "~2.3.3" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" tunnel-agent "^0.6.0" - uuid "^3.1.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= -rimraf@2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" - integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w== - dependencies: - glob "^7.0.5" - rimraf@^2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +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.5.1: version "5.5.1" @@ -1552,21 +572,14 @@ semver@5.5.1: integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== semver@^5.3.0, semver@^5.4.1: - version "5.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" - integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== - -sntp@2.x.x: - version "2.1.0" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" - integrity sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg== - dependencies: - hoek "4.x.x" + 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.5" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.5.tgz#0d4af9e00493e855402e8ec36ebed2d266fceb90" - integrity sha512-mR7/Nd5l1z6g99010shcXJiNEaf3fEtmLhRB/sBcQVJGodcHCULPp2y4Sfa43Kv2zq7T+Izmfp/WHCR6dYkQCA== + 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" @@ -1576,168 +589,34 @@ source-map@^0.6.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sparkles@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" - integrity sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM= - -split@0.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" - integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= - dependencies: - through "2" - sshpk@^1.7.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" - integrity sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s= + 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" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: 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" -stat-mode@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-0.2.2.tgz#e6c80b623123d7d80cf132ce538f346289072502" - integrity sha1-5sgLYjEj19gM8TLOU480YokHJQI= - -stream-combiner@~0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" - integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= +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: - duplexer "~0.1.1" + has-flag "^3.0.0" -stream-shift@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" - integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= - -streamfilter@^1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/streamfilter/-/streamfilter-1.0.7.tgz#ae3e64522aa5a35c061fd17f67620c7653c643c9" - integrity sha512-Gk6KZM+yNA1JpW0KzlZIhjo3EaBJDkYfXtYSbOwNIQ7Zd6006E6+sCFlW1NDvFG/vnXhKmw6TJJgiEQg/8lXfQ== - dependencies: - readable-stream "^2.0.2" - -streamifier@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/streamifier/-/streamifier-0.1.1.tgz#97e98d8fa4d105d62a2691d1dc07e820db8dfc4f" - integrity sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8= - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -stringstream@~0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" - integrity sha1-TkhM1N5aC7vuGORjB3EKioFiGHg= - -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-bom-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz#e7144398577d51a6bed0fa1994fa05f43fd988ee" - integrity sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4= - dependencies: - first-chunk-stream "^1.0.0" - strip-bom "^2.0.0" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - -supports-color@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" - integrity sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ== - dependencies: - has-flag "^2.0.0" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -tar@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - integrity sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE= - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" - -through2-filter@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-2.0.0.tgz#60bc55a0dacb76085db1f9dae99ab43f83d622ec" - integrity sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw= - dependencies: - through2 "~2.0.0" - xtend "~4.0.0" - -through2@^0.6.0, through2@~0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" - integrity sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg= - dependencies: - readable-stream ">=1.0.33-1 <1.1.0-0" - xtend ">=4.0.0 <4.1.0-0" - -through2@^2.0.0, through2@^2.0.3, through2@~2.0.0, through2@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" - integrity sha1-AARWmzfHx0ujnEPzzteNGtlBQL4= - dependencies: - readable-stream "^2.1.5" - xtend "~4.0.1" - -through@2, through@~2.3, through@~2.3.1: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -time-stamp@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" - integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= - -to-absolute-glob@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz#1cdfa472a9ef50c239ee66999b662ca0eb39937f" - integrity sha1-HN+kcqnvUMI57maZm2YsoOs5k38= - dependencies: - extend-shallow "^2.0.1" - -tough-cookie@~2.3.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" - integrity sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA== +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: @@ -1752,36 +631,25 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -unique-stream@^2.0.2: - version "2.2.1" - resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.2.1.tgz#5aa003cfbe94c5ff866c4e7d668bb1c4dbadb369" - integrity sha1-WqADz76Uxf+GbE59ZouxxNuts2k= +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: - json-stable-stringify "^1.0.0" - through2-filter "^2.0.0" + punycode "^2.1.0" -url-parse@^1.1.9: - version "1.4.0" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.0.tgz#6bfdaad60098c7fe06f623e42b22de62de0d3d75" - integrity sha512-ERuGxDiQ6Xw/agN4tuoCRbmwRuZP0cJ1lJxJubXr5Q/5cDa78+Dc4wfvtxzhzhkm5VvmW6Mf8EVj9SPGN4l8Lg== +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.0.0" + querystringify "^2.1.1" requires-port "^1.0.0" -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -uuid@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" - integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA== - -vali-date@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/vali-date/-/vali-date-1.0.0.tgz#1b904a59609fb328ef078138420934f6b86709a6" - integrity sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY= +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" @@ -1792,75 +660,6 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vinyl-fs@^2.0.0, vinyl-fs@^2.4.3: - version "2.4.4" - resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-2.4.4.tgz#be6ff3270cb55dfd7d3063640de81f25d7532239" - integrity sha1-vm/zJwy1Xf19MGNkDegfJddTIjk= - dependencies: - duplexify "^3.2.0" - glob-stream "^5.3.2" - graceful-fs "^4.0.0" - gulp-sourcemaps "1.6.0" - is-valid-glob "^0.3.0" - lazystream "^1.0.0" - lodash.isequal "^4.0.0" - merge-stream "^1.0.0" - mkdirp "^0.5.0" - object-assign "^4.0.0" - readable-stream "^2.0.4" - strip-bom "^2.0.0" - strip-bom-stream "^1.0.0" - through2 "^2.0.0" - through2-filter "^2.0.0" - vali-date "^1.0.0" - vinyl "^1.0.0" - -vinyl-source-stream@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vinyl-source-stream/-/vinyl-source-stream-1.1.2.tgz#62b53a135610a896e98ca96bee3a87f008a8e780" - integrity sha1-YrU6E1YQqJbpjKlr7jqH8Aio54A= - dependencies: - through2 "^2.0.3" - vinyl "^0.4.3" - -vinyl@^0.4.3, vinyl@~0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" - integrity sha1-LzVsh6VQolVGHza76ypbqL94SEc= - dependencies: - clone "^0.2.0" - clone-stats "^0.0.1" - -vinyl@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" - integrity sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4= - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" - integrity sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ= - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^2.0.1, vinyl@^2.0.2: - version "2.1.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.1.0.tgz#021f9c2cf951d6b939943c89eb5ee5add4fd924c" - integrity sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw= - dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - vscode-extension-telemetry@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" @@ -1869,55 +668,36 @@ vscode-extension-telemetry@0.1.1: applicationinsights "1.0.8" vscode-nls@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" - integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" + integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== + +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.10: - version "1.1.17" - resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.17.tgz#cc2a61731e925301f03f003c009cbf454022cd83" - integrity sha512-yNMyrgEua2qyW7+trNNYhA6PeldRrBcwtLtlazkdtzcmkHMKECM/08bPF8HF2ZFuwHgD+8FQsdqd/DvJYQYjJg== + 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" - gulp-chmod "^2.0.0" - gulp-filter "^5.0.1" - gulp-gunzip "1.0.0" - gulp-remote-src-vscode "^0.5.0" - gulp-symdest "^1.1.0" - gulp-untar "^0.0.6" - gulp-vinyl-zip "^2.1.0" - mocha "^4.0.1" - request "^2.83.0" + mocha "^5.2.0" + request "^2.88.0" semver "^5.4.1" source-map-support "^0.5.0" - url-parse "^1.1.9" - vinyl-source-stream "^1.1.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= -"xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.0, xtend@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= - -yauzl@^2.2.1: - version "2.9.1" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.9.1.tgz#a81981ea70a57946133883f029c5821a89359a7f" - integrity sha1-qBmB6nCleUYTOIPwKcWCGok1mn8= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.0.1" - -yazl@^2.2.1: - version "2.4.3" - resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.4.3.tgz#ec26e5cc87d5601b9df8432dbdd3cd2e5173a071" - integrity sha1-7CblzIfVYBud+EMtvdPNLlFzoHE= - dependencies: - buffer-crc32 "~0.2.3" - zone.js@0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" From 900100b745fee5a53be67930406aee16d0f2c2f5 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 9 Jan 2020 23:23:55 +0100 Subject: [PATCH 090/843] use typescript-vscode-sh-plugin --- extensions/typescript-language-features/package.json | 2 +- extensions/typescript-language-features/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 3191e9fc274..df72fe3d01e 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -21,7 +21,7 @@ "semver": "5.5.1", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0", - "@aeschli/typescript-vscode-sh-plugin": "0.2.0" + "typescript-vscode-sh-plugin": "0.2.0" }, "devDependencies": { "@types/node": "^12.11.7", diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index 054c7c6f146..6f2da650dd8 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -2,11 +2,6 @@ # yarn lockfile v1 -"@aeschli/typescript-vscode-sh-plugin@0.2.0": - version "0.2.0" - resolved "https://npm.pkg.github.com/download/@aeschli/typescript-vscode-sh-plugin/0.2.0/6e1ccd1d9ed6c3251091de08bb83589bc555f203b1c594fc9c2bc9d38b66b44d#" - integrity sha512-QHORCwbuln5T9gdfDoqJ2nNXzY/8yOBgTG2f+GiQPeQ0rGrNIydbKe0IYi7VmCvIm1LGhEcNOihcF4FWmsKFow== - "@types/events@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -631,6 +626,11 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +typescript-vscode-sh-plugin@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.2.0.tgz#8f1b79f2bc5a7d225235bf2b9fffdec9499f00f7" + integrity sha512-jp3B45VPwBuJ7I7IshkNuxn92/DUPqFq6Y3wiNE64Mmws0UCkXKbTsDT8+CKt5rFFCQL7PUtApj6aQkN8OKfxw== + uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" From ad97bd7493758e5337b9344a88d35102e0e719db Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 9 Jan 2020 14:25:39 -0800 Subject: [PATCH 091/843] Exclude tsconfig files under dot file directories Fixes #88328 --- .../typescript-language-features/src/utils/tsconfigProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/utils/tsconfigProvider.ts b/extensions/typescript-language-features/src/utils/tsconfigProvider.ts index 04843d0058a..e77dfb306a9 100644 --- a/extensions/typescript-language-features/src/utils/tsconfigProvider.ts +++ b/extensions/typescript-language-features/src/utils/tsconfigProvider.ts @@ -18,7 +18,7 @@ export default class TsConfigProvider { return []; } const configs = new Map(); - for (const config of await vscode.workspace.findFiles('**/tsconfig*.json', '**/node_modules/**')) { + for (const config of await vscode.workspace.findFiles('**/tsconfig*.json', '**/{node_modules,.*}/**')) { const root = vscode.workspace.getWorkspaceFolder(config); if (root) { configs.set(config.fsPath, { From c8123dee962847abcbd988d8c180ae1dec0b56d5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 9 Jan 2020 15:06:11 -0800 Subject: [PATCH 092/843] Fix webviews for electron 7 Caused by #83796 Also re-enables the webview tests --- .../vscode-api-tests/src/singlefolder-tests/webview.test.ts | 2 +- src/vs/code/electron-main/app.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts index e149da8995b..e785f1d4afb 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -13,7 +13,7 @@ const webviewId = 'myWebview'; const testDocument = join(vscode.workspace.rootPath || '', './bower.json'); -suite.skip('Webview tests', () => { +suite('Webview tests', () => { const disposables: vscode.Disposable[] = []; function _register(disposable: T) { diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 549c348240b..712d6f05c5b 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -191,7 +191,8 @@ export class CodeApplication extends Disposable { webPreferences.nodeIntegration = false; // Verify URLs being loaded - if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preload)) { + // https://github.com/electron/electron/issues/21553 + if (isValidWebviewSource(params.src) && isValidWebviewSource((webPreferences as { preloadURL: string }).preloadURL)) { return; } From efd7f099d9a572bacb5a6a37c286093c85a40a7c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 10 Jan 2020 00:44:02 +0100 Subject: [PATCH 093/843] Fix issues around surrogate pairs --- .../characterHardWrappingLineMapper.ts | 229 ++++++++++-------- .../characterHardWrappingLineMapper.test.ts | 40 ++- 2 files changed, 173 insertions(+), 96 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 91c4071798c..5f36638ef6f 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -104,6 +104,9 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC return null; } + const prevBreakingOffsets = previousBreakingData.breakOffsets; + const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakingOffsetsVisibleColumn; + const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); const wrappedLineBreakingColumn = firstLineBreakingColumn - wrappedTextIndentLength; @@ -111,21 +114,18 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC let breakingOffsetsVisibleColumn: number[] = []; let breakingOffsetsCount: number = 0; - const prevBreakingOffsets = previousBreakingData.breakOffsets; - const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakingOffsetsVisibleColumn; - let breakingColumn = firstLineBreakingColumn; const prevLen = prevBreakingOffsets.length; let prevIndex = 0; if (prevIndex >= 0) { - let currentDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); + let bestDistance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); while (prevIndex + 1 < prevLen) { - const potentialDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); - if (potentialDiff >= currentDiff) { + const distance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); + if (distance >= bestDistance) { break; } - currentDiff = potentialDiff; + bestDistance = distance; prevIndex++; } } @@ -133,7 +133,7 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC while (prevIndex < prevLen) { // Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break) const prevBreakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex]; - const prevBreakoffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex];; + const prevBreakoffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex]; let breakOffset = 0; let breakOffsetVisibleColumn = 0; @@ -148,29 +148,32 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC let prevCharCodeClass = classifier.get(prevCharCode); let entireLineFits = true; for (let i = prevBreakOffset; i < len; i++) { + const charStartOffset = i; const charCode = lineText.charCodeAt(i); - const charCodeClass = classifier.get(charCode); + let charCodeClass: number; + let charWidth: number; - if (strings.isHighSurrogate(prevCharCode)) { + if (strings.isHighSurrogate(charCode)) { // A surrogate pair must always be considered as a single unit, so it is never to be broken - visibleColumn += 1; - prevCharCode = charCode; - prevCharCodeClass = charCodeClass; - continue; - + i++; + charCodeClass = CharacterClass.NONE; + charWidth = 2; + } else { + charCodeClass = classifier.get(charCode); + charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); } if (canBreak(prevCharCodeClass, charCodeClass)) { - breakOffset = i; + breakOffset = charStartOffset; breakOffsetVisibleColumn = visibleColumn; } - const charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); visibleColumn += charWidth; + // check if adding character at `i` will go over the breaking column if (visibleColumn > breakingColumn) { // We need to break at least before character at `i`: - forcedBreakOffset = i; + forcedBreakOffset = charStartOffset; forcedBreakOffsetVisibleColumn = visibleColumn - charWidth; if (visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { @@ -188,9 +191,11 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC if (entireLineFits) { // there is no more need to break => stop the outer loop! - // Add last segment - breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1]; - breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1]; + if (breakingOffsetsCount > 0) { + // Add last segment + breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1]; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1]; + } break; } } @@ -202,16 +207,8 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC let charCodeClass = classifier.get(charCode); let hitATabCharacter = false; for (let i = prevBreakOffset - 1; i >= 0; i--) { + const charStartOffset = i + 1; const prevCharCode = lineText.charCodeAt(i); - const prevCharCodeClass = classifier.get(prevCharCode); - - if (strings.isHighSurrogate(prevCharCode)) { - // A surrogate pair must always be considered as a single unit, so it is never to be broken - visibleColumn -= 1; - charCode = prevCharCode; - charCodeClass = prevCharCodeClass; - continue; - } if (prevCharCode === CharCode.Tab) { // cannot determine the width of a tab when going backwards, so we must go forwards @@ -219,11 +216,22 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC break; } - const charWidth = (strings.isFullWidthCharacter(prevCharCode) ? columnsForFullWidthChar : 1); + let prevCharCodeClass: number; + let prevCharWidth: number; + + if (strings.isLowSurrogate(prevCharCode)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + i--; + prevCharCodeClass = CharacterClass.NONE; + prevCharWidth = 2; + } else { + prevCharCodeClass = classifier.get(prevCharCode); + prevCharWidth = (strings.isFullWidthCharacter(prevCharCode) ? columnsForFullWidthChar : 1); + } if (visibleColumn <= breakingColumn) { if (forcedBreakOffset === 0) { - forcedBreakOffset = i + 1; + forcedBreakOffset = charStartOffset; forcedBreakOffsetVisibleColumn = visibleColumn; } @@ -233,17 +241,35 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC } if (canBreak(prevCharCodeClass, charCodeClass)) { - breakOffset = i + 1; + breakOffset = charStartOffset; breakOffsetVisibleColumn = visibleColumn; break; } } - visibleColumn -= charWidth; + visibleColumn -= prevCharWidth; charCode = prevCharCode; charCodeClass = prevCharCodeClass; } + if (breakOffset !== 0) { + const remainingWidthOfNextLine = wrappedLineBreakingColumn - (forcedBreakOffsetVisibleColumn - breakOffsetVisibleColumn); + if (remainingWidthOfNextLine <= tabSize) { + const charCodeAtForcedBreakOffset = lineText.charCodeAt(forcedBreakOffset); + let charWidth: number; + if (strings.isHighSurrogate(charCodeAtForcedBreakOffset)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + charWidth = 2; + } else { + charWidth = computeCharWidth(charCodeAtForcedBreakOffset, forcedBreakOffsetVisibleColumn, tabSize, columnsForFullWidthChar); + } + if (remainingWidthOfNextLine - charWidth < 0) { + // it is not worth it to break at breakOffset, it just introduces an extra needless line! + breakOffset = 0; + } + } + } + if (hitATabCharacter) { // cannot determine the width of a tab when going backwards, so we must go forwards from the previous break prevIndex--; @@ -266,14 +292,13 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC prevIndex++; } - let currentDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); - // let lastBreakingColumn + let bestDistance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); while (prevIndex + 1 < prevLen) { - const potentialDiff = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); - if (potentialDiff >= currentDiff) { + const distance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); + if (distance >= bestDistance) { break; } - currentDiff = potentialDiff; + bestDistance = distance; prevIndex++; } } @@ -282,54 +307,56 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC return null; } - // return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); - const expected = createLineMapping(classifier, lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); - const actual = new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); - try { - actual.assertEqual(expected); - } catch (err) { - console.log(`BREAKING!!`); - console.log(err); - console.log(`previous breaks: ${JSON.stringify(prevBreakingOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(prevBreakingOffsetsVisibleColumn)}`); - console.log(`expected breakOffsets: ${JSON.stringify(expected?.breakOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(expected?.breakingOffsetsVisibleColumn)}`); - console.log(`actual breakOffsets: ${JSON.stringify(actual?.breakOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(actual?.breakingOffsetsVisibleColumn)}`); + return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); + // const expected = createLineMapping(classifier, lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); + // const actual = new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); + // try { + // actual.assertEqual(expected); + // } catch (err) { + // console.log(`BREAKING!!`); + // console.log(err); + // console.log(` + // firstLineBreakingColumn: ${firstLineBreakingColumn} - console.log(`actual str: ${toAnnotatedText(lineText, actual)}`); + // previous breaks: ${JSON.stringify(prevBreakingOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(prevBreakingOffsetsVisibleColumn)} + // expected breaks: ${JSON.stringify(expected?.breakOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(expected?.breakingOffsetsVisibleColumn)} + // actual breaks: ${JSON.stringify(actual?.breakOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(actual?.breakingOffsetsVisibleColumn)} + // previous str: ${toAnnotatedText(lineText, previousBreakingData)} + // expected str: ${toAnnotatedText(lineText, expected)} + // actual str: ${toAnnotatedText(lineText, actual)} - console.log(` - assertIncrementalLineMapping( - factory, ${str(lineText)}, 4, - ${previousBreakingData.breakingColumn}, ${str(toAnnotatedText(lineText, previousBreakingData))}, - ${expected!.breakingColumn}, ${str(toAnnotatedText(lineText, expected))}, - WrappingIndent.${hardWrappingIndent === WrappingIndent.None ? 'None' : hardWrappingIndent === WrappingIndent.Same ? 'Same' : hardWrappingIndent === WrappingIndent.Indent ? 'Indent' : 'DeepIndent'} - ); -`); - function str(strr: string) { - return `'${strr.replace(/'/g, '\\\'')}'`; - } - function toAnnotatedText(text: string, lineBreakingData: LineBreakingData | null): string { - // Insert line break markers again, according to algorithm - let actualAnnotatedText = ''; - if (lineBreakingData) { - let previousLineIndex = 0; - for (let i = 0, len = text.length; i < len; i++) { - let r = LineBreakingData.getOutputPositionOfInputOffset(lineBreakingData.breakOffsets, i); - if (previousLineIndex !== r.outputLineIndex) { - previousLineIndex = r.outputLineIndex; - actualAnnotatedText += '|'; - } - actualAnnotatedText += text.charAt(i); - } - } else { - // No wrapping - actualAnnotatedText = text; - } - return actualAnnotatedText; - } - } - return actual; - + // assertIncrementalLineMapping( + // factory, ${str(lineText)}, 4, + // ${previousBreakingData.breakingColumn}, ${str(toAnnotatedText(lineText, previousBreakingData))}, + // ${expected!.breakingColumn}, ${str(toAnnotatedText(lineText, expected))}, + // WrappingIndent.${hardWrappingIndent === WrappingIndent.None ? 'None' : hardWrappingIndent === WrappingIndent.Same ? 'Same' : hardWrappingIndent === WrappingIndent.Indent ? 'Indent' : 'DeepIndent'} + // ); + // `); + // function str(strr: string) { + // return `'${strr.replace(/\\/g, '\\\\').replace(/'/g, '\\\'')}'`; + // } + // function toAnnotatedText(text: string, lineBreakingData: LineBreakingData | null): string { + // // Insert line break markers again, according to algorithm + // let actualAnnotatedText = ''; + // if (lineBreakingData) { + // let previousLineIndex = 0; + // for (let i = 0, len = text.length; i < len; i++) { + // let r = LineBreakingData.getOutputPositionOfInputOffset(lineBreakingData.breakOffsets, i); + // if (previousLineIndex !== r.outputLineIndex) { + // previousLineIndex = r.outputLineIndex; + // actualAnnotatedText += '|'; + // } + // actualAnnotatedText += text.charAt(i); + // } + // } else { + // // No wrapping + // actualAnnotatedText = text; + // } + // return actualAnnotatedText; + // } + // } + // return actual; } function createLineMapping(classifier: WrappingCharacterClassifier, lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { @@ -356,24 +383,36 @@ function createLineMapping(classifier: WrappingCharacterClassifier, lineText: st let prevCharCodeClass = classifier.get(prevCharCode); let visibleColumn = computeCharWidth(prevCharCode, 0, tabSize, columnsForFullWidthChar); - for (let i = 1; i < len; i++) { - const charCode = lineText.charCodeAt(i); - const charCodeClass = classifier.get(charCode); + let startOffset = 1; + if (strings.isHighSurrogate(prevCharCode)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + visibleColumn += 1; + prevCharCode = lineText.charCodeAt(1); + prevCharCodeClass = classifier.get(prevCharCode); + startOffset++; + } - if (strings.isHighSurrogate(prevCharCode)) { + for (let i = startOffset; i < len; i++) { + const charStartOffset = i; + const charCode = lineText.charCodeAt(i); + let charCodeClass: number; + let charWidth: number; + + if (strings.isHighSurrogate(charCode)) { // A surrogate pair must always be considered as a single unit, so it is never to be broken - visibleColumn += 1; - prevCharCode = charCode; - prevCharCodeClass = charCodeClass; - continue; + i++; + charCodeClass = CharacterClass.NONE; + charWidth = 2; + } else { + charCodeClass = classifier.get(charCode); + charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); } if (canBreak(prevCharCodeClass, charCodeClass)) { - breakOffset = i; + breakOffset = charStartOffset; breakOffsetVisibleColumn = visibleColumn; } - const charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); visibleColumn += charWidth; // check if adding character at `i` will go over the breaking column @@ -382,7 +421,7 @@ function createLineMapping(classifier: WrappingCharacterClassifier, lineText: st if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { // Cannot break at `breakOffset`, must break at `i` - breakOffset = i; + breakOffset = charStartOffset; breakOffsetVisibleColumn = visibleColumn - charWidth; } diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts index 7d141bfdad0..ef61ca5b183 100644 --- a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts +++ b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts @@ -154,6 +154,34 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { 16, '\t\t"owner":| |"vscode"|,', WrappingIndent.Same ); + + assertIncrementalLineMapping( + factory, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇&👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬', 4, + 51, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇&|👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬', + 50, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇|&|👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬', + WrappingIndent.Same + ); + + assertIncrementalLineMapping( + factory, '🐇👬&🌞🌖', 4, + 5, '🐇👬&|🌞🌖', + 4, '🐇👬|&|🌞🌖', + WrappingIndent.Same + ); + + assertIncrementalLineMapping( + factory, '\t\tfunc(\'🌞🏇🍼🌞🏇🍼🐇&👬🌖🌞👬🌖🌞🏇🍼🐇👬\', WrappingIndent.Same);', 4, + 26, '\t\tfunc|(\'🌞🏇🍼🌞🏇🍼🐇&|👬🌖🌞👬🌖🌞🏇🍼🐇|👬\', |WrappingIndent.|Same);', + 27, '\t\tfunc|(\'🌞🏇🍼🌞🏇🍼🐇&|👬🌖🌞👬🌖🌞🏇🍼🐇|👬\', |WrappingIndent.|Same);', + WrappingIndent.Same + ); + + assertIncrementalLineMapping( + factory, 'factory, "xtxtfunc(x"🌞🏇🍼🌞🏇🍼🐇&👬🌖🌞👬🌖🌞🏇🍼🐇👬x"', 4, + 16, 'factory, |"xtxtfunc|(x"🌞🏇🍼🌞🏇🍼|🐇&|👬🌖🌞👬🌖🌞🏇🍼|🐇👬x"', + 17, 'factory, |"xtxtfunc|(x"🌞🏇🍼🌞🏇🍼🐇|&👬🌖🌞👬🌖🌞🏇🍼|🐇👬x"', + WrappingIndent.Same + ); }); @@ -185,7 +213,17 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { test('issue #75494: surrogate pairs', () => { let factory = new CharacterHardWrappingLineMapperFactory('\t', ' '); - assertLineMapping(factory, 4, 49, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇|👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬', WrappingIndent.Same); + assertLineMapping(factory, 4, 49, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼|🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼|🐇👬', WrappingIndent.Same); + }); + + test('issue #75494: surrogate pairs overrun 1', () => { + const factory = new CharacterHardWrappingLineMapperFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); + assertLineMapping(factory, 4, 4, '🐇👬|&|🌞🌖', WrappingIndent.Same); + }); + + test('issue #75494: surrogate pairs overrun 2', () => { + const factory = new CharacterHardWrappingLineMapperFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); + assertLineMapping(factory, 4, 17, 'factory, |"xtxtfunc|(x"🌞🏇🍼🌞🏇🍼🐇|&👬🌖🌞👬🌖🌞🏇🍼|🐇👬x"', WrappingIndent.Same); }); test('CharacterHardWrappingLineMapper - WrappingIndent.DeepIndent', () => { From 326914ae98a957f9fefd77e252e64b438adee08a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 10 Jan 2020 01:00:14 +0100 Subject: [PATCH 094/843] Avoid object churn --- .../characterHardWrappingLineMapper.ts | 20 ++++++++++++++++--- .../common/viewModel/splitLinesCollection.ts | 8 ++++---- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 5f36638ef6f..6d78f602fd2 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -94,6 +94,8 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor } } +let arrPool1: number[] = []; +let arrPool2: number[] = []; function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterClassifier, previousBreakingData: LineBreakingData, lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { if (firstLineBreakingColumn === -1) { return null; @@ -110,8 +112,8 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); const wrappedLineBreakingColumn = firstLineBreakingColumn - wrappedTextIndentLength; - let breakingOffsets: number[] = []; - let breakingOffsetsVisibleColumn: number[] = []; + let breakingOffsets: number[] = arrPool1; + let breakingOffsetsVisibleColumn: number[] = arrPool2; let breakingOffsetsCount: number = 0; let breakingColumn = firstLineBreakingColumn; @@ -307,7 +309,19 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC return null; } - return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); + // Doing here some object reuse which ends up helping a huge deal with GC pauses! + breakingOffsets.length = breakingOffsetsCount; + breakingOffsetsVisibleColumn.length = breakingOffsetsCount; + arrPool1 = previousBreakingData.breakOffsets; + arrPool2 = previousBreakingData.breakingOffsetsVisibleColumn; + previousBreakingData.breakingColumn = firstLineBreakingColumn; + previousBreakingData.breakOffsets = breakingOffsets; + previousBreakingData.breakingOffsetsVisibleColumn = breakingOffsetsVisibleColumn; + previousBreakingData.wrappedTextIndentLength = wrappedTextIndentLength; + return previousBreakingData; + + // return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); + // const expected = createLineMapping(classifier, lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); // const actual = new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); // try { diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 2f0af80ac24..90f3c4c20d8 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -28,10 +28,10 @@ export class OutputPosition { export class LineBreakingData { constructor( - public readonly breakingColumn: number, - public readonly breakOffsets: number[], - public readonly breakingOffsetsVisibleColumn: number[], - public readonly wrappedTextIndentLength: number + public breakingColumn: number, + public breakOffsets: number[], + public breakingOffsetsVisibleColumn: number[], + public wrappedTextIndentLength: number ) { } assertEqual(other: LineBreakingData | null): void { From 1d752b1e25126a13256a433ee4559c3b360f8abd Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 9 Jan 2020 16:52:00 -0800 Subject: [PATCH 095/843] Hook up basic custom editor update on rename For #86599 --- .../customEditor/browser/customEditors.ts | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 52d8f474f43..908c38979fc 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -13,6 +13,7 @@ import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { FileOperation, IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; @@ -25,7 +26,7 @@ import { CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CONTEXT_HAS_CUSTOM_EDITORS, import { CustomEditorModelManager } from 'vs/workbench/contrib/customEditor/common/customEditorModelManager'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { IWebviewService, webviewHasOwnEditFunctionsContext } from 'vs/workbench/contrib/webview/browser/webview'; -import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { CustomFileEditorInput } from './customEditorInput'; @@ -81,8 +82,10 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ constructor( @IContextKeyService contextKeyService: IContextKeyService, @IWorkingCopyService workingCopyService: IWorkingCopyService, + @IFileService fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorService private readonly editorService: IEditorService, + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IWebviewService private readonly webviewService: IWebviewService, @@ -112,6 +115,13 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ this._webviewHasOwnEditFunctions = webviewHasOwnEditFunctionsContext.bindTo(contextKeyService); this._register(this.editorService.onDidActiveEditorChange(() => this.updateContexts())); + + this._register(fileService.onAfterOperation(e => { + if (e.isOperation(FileOperation.MOVE)) { + this.handleMovedFileInOpenedFileEditors(e.resource, e.target.resource); + } + })); + this.updateContexts(); } @@ -262,6 +272,31 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ this._focusedCustomEditorIsEditable.set(activeControl?.input instanceof CustomFileEditorInput); this._webviewHasOwnEditFunctions.set(true); } + + private handleMovedFileInOpenedFileEditors(oldResource: URI, newResource: URI): void { + for (const group of this.editorGroupService.groups) { + for (const editor of group.editors) { + if (!(editor instanceof CustomFileEditorInput)) { + continue; + } + + const editorInfo = this._editorInfoStore.get(editor.viewType); + if (!editorInfo) { + continue; + } + + if (!editorInfo.matches(newResource)) { + continue; + } + + const replacement = this.createInput(newResource, editor.viewType, group); + this.editorService.replaceEditors([{ + editor: editor, + replacement: replacement, + }], group); + } + } + } } export const customEditorsAssociationsKey = 'workbench.experimental.editorAssociations'; From 960cddb992cc96d47f56b633b361909f1b5b3353 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 9 Jan 2020 17:31:42 -0800 Subject: [PATCH 096/843] fix #88394. --- .../contrib/welcome/walkThrough/browser/walkThroughPart.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index 149c916f53d..add87f122e0 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -421,7 +421,8 @@ export class WalkThroughPart extends BaseEditor { horizontal: 'auto', useShadows: true, verticalHasArrows: false, - horizontalHasArrows: false + horizontalHasArrows: false, + alwaysConsumeMouseWheel: false }, overviewRulerLanes: 3, fixedOverflowWidgets: true, From 95b6bb00e2e87535a19ca22273722d1d96ccdcc1 Mon Sep 17 00:00:00 2001 From: kieferrm Date: Thu, 9 Jan 2020 20:04:07 -0800 Subject: [PATCH 097/843] issue flow configuration --- .github/feature-requests.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/feature-requests.yml diff --git a/.github/feature-requests.yml b/.github/feature-requests.yml new file mode 100644 index 00000000000..18055b84486 --- /dev/null +++ b/.github/feature-requests.yml @@ -0,0 +1,34 @@ +{ + typeLabel: { + name: 'feature-request' + }, + candidateMilestone: { + number: 107, + name: 'Backlog Candidates' + }, + approvedMilestone: { + number: 8, + name: 'Backlog' + }, + onLabeled: { + delay: 60, + perform: true + }, + onCandidateMilestoned: { + candidatesComment: "This feature request is now a candidate for our backlog. The community has 60 days to upvote the issue. If it receives 20 upvotes we will move it to our backlog. If not, we will close it. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!", + perform: true + }, + onMonitorUpvotes: { + upvoteThreshold: 20, + acceptanceComment: ":slightly_smiling_face: This feature request received a sufficient number of community upvotes and we moved it to our backlog. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!", + perform: true + }, + onMonitorDaysOnCandidateMilestone: { + daysOnMilestone: 60, + warningPeriod: 10, + numberOfCommentsToPreventAutomaticRejection: 20, + rejectionComment: ":slightly_frowning_face: In the last 60 days, this feature request has received less than 20 community upvotes and we closed it. Still a big Thank You to you for taking the time to create this issue! To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!", + warningComment: "This feature request has not yet received the 20 community upvotes it takes to make to our backlog. 10 days to go. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding", + perform: true + } +} From 3e6ba164cce3f643b560eef8dbcf5ec32a4e22ca Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 07:00:00 +0100 Subject: [PATCH 098/843] show preview only for rename --- src/vs/editor/browser/services/bulkEditService.ts | 2 +- src/vs/editor/contrib/rename/rename.ts | 2 +- src/vs/workbench/api/browser/mainThreadEditors.ts | 2 +- src/vs/workbench/contrib/search/browser/replaceService.ts | 2 +- src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index 7c433166856..0aecb86edfa 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -14,7 +14,7 @@ export const IBulkEditService = createDecorator('IWorkspaceEdi export interface IBulkEditOptions { editor?: ICodeEditor; progress?: IProgress; - noPreview?: boolean; + showPreview?: boolean; } export interface IBulkEditResult { diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 29b4bb8f7cf..20ef921a6c1 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -200,7 +200,7 @@ class RenameController implements IEditorContribution { this._bulkEditService.apply(renameResult, { editor: this.editor, - noPreview: !inputFieldResult.wantsPreview + showPreview: inputFieldResult.wantsPreview }).then(result => { if (result.ariaSummary) { alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, inputFieldResult.newName, result.ariaSummary)); diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index 421201a9c64..8dd058290d0 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -217,7 +217,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { $tryApplyWorkspaceEdit(dto: IWorkspaceEditDto): Promise { const { edits } = reviveWorkspaceEditDto(dto); - return this._bulkEditService.apply({ edits }, { noPreview: false }).then(() => true, err => false); + return this._bulkEditService.apply({ edits }).then(() => true, _err => false); } $tryInsertSnippet(id: string, template: string, ranges: readonly IRange[], opts: IUndoStopOptions): Promise { diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index 92a9cf4b56b..bc7e14ab2e2 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -105,7 +105,7 @@ export class ReplaceService implements IReplaceService { replace(match: FileMatchOrMatch, progress?: IProgress, resource?: URI): Promise; replace(arg: any, progress: IProgress | undefined = undefined, resource: URI | null = null): Promise { const edits: ResourceTextEdit[] = this.createEdits(arg, resource); - return this.bulkEditorService.apply({ edits }, { progress, noPreview: true }).then(() => this.textFileService.saveAll(edits.map(e => e.resource))); + return this.bulkEditorService.apply({ edits }, { progress }).then(() => this.textFileService.saveAll(edits.map(e => e.resource))); } openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 7d543daaadd..5808f9a78fd 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -406,7 +406,7 @@ export class BulkEditService implements IBulkEditService { async apply(edit: WorkspaceEdit, options?: IBulkEditOptions): Promise { - if (this._previewHandler && !options?.noPreview) { + if (this._previewHandler && options?.showPreview) { edit = await this._previewHandler(edit, options); } From 7c6a2a7889bfd386cab902dbec83f0fc2f698139 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 07:21:34 +0100 Subject: [PATCH 099/843] show panel only while refactoring preview is active --- .../bulkEdit/browser/bulkEdit.contribution.ts | 58 ++++++++++++------- .../contrib/bulkEdit/browser/bulkEditPane.ts | 2 +- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index 1882e742a44..dffe6a8715b 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -14,39 +14,52 @@ import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewCon import { localize } from 'vs/nls'; import { ViewPaneContainer, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { PaneCompositePanel } from 'vs/workbench/browser/panel'; +import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; class BulkEditPreviewContribution { + static readonly ctxEnabled = new RawContextKey('refactorPreviewIsEnabled', false); + + private readonly _ctxEnabled: IContextKey; + constructor( @IPanelService private _panelService: IPanelService, @IBulkEditService bulkEditService: IBulkEditService, + @IContextKeyService contextKeyService: IContextKeyService, ) { bulkEditService.setPreviewHandler(edit => this._previewEdit(edit)); + this._ctxEnabled = BulkEditPreviewContribution.ctxEnabled.bindTo(contextKeyService); } private async _previewEdit(edit: WorkspaceEdit) { + this._ctxEnabled.set(true); + try { - let view: ViewPane | undefined; - const activePanel = this._panelService.openPanel(BulkEditPane.ID, true); - if (activePanel instanceof PaneCompositePanel) { - view = activePanel.getViewPaneContainer().getView(BulkEditPane.ID); + let view: ViewPane | undefined; + const activePanel = this._panelService.openPanel(BulkEditPane.ID, true); + if (activePanel instanceof PaneCompositePanel) { + view = activePanel.getViewPaneContainer().getView(BulkEditPane.ID); + } + + if (!(view instanceof BulkEditPane)) { + return edit; + } + + const newEditOrUndefined = await view.setInput(edit); + if (!newEditOrUndefined) { + return { edits: [] }; + } + + // todo@joh/steVen add view state + // if (this._panelService.getLastActivePanelId() === BulkEditPanel.ID) { + // this._panelService.hideActivePanel(); + // } + + return newEditOrUndefined; + + } finally { + this._ctxEnabled.set(false); } - - if (!(view instanceof BulkEditPane)) { - return edit; - } - - const newEditOrUndefined = await view.setInput(edit); - if (!newEditOrUndefined) { - return { edits: [] }; - } - - // todo@joh/steVen add view state - // if (this._panelService.getLastActivePanelId() === BulkEditPanel.ID) { - // this._panelService.hideActivePanel(); - // } - - return newEditOrUndefined; } } @@ -58,16 +71,17 @@ Registry.as(WorkbenchExtensions.Workbench).regi const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: BulkEditPane.ID, name: localize('panel', "Refactor Preview"), + hideIfEmpty: true, ctorDescriptor: { ctor: ViewPaneContainer, - arguments: [BulkEditPane.ID, '', { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }] + arguments: [BulkEditPane.ID, BulkEditPane.ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }] } }, ViewContainerLocation.Panel); Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ id: BulkEditPane.ID, name: localize('panel', "Refactor Preview"), - canToggleVisibility: false, + when: BulkEditPreviewContribution.ctxEnabled, ctorDescriptor: { ctor: BulkEditPane }, }], container); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 1982c15c8d5..e4b5a81d91c 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -32,7 +32,7 @@ const enum State { export class BulkEditPanel extends ViewPane { - static readonly ID = 'BulkEditPanel'; + static readonly ID = 'refactorPreview'; private _tree!: WorkbenchAsyncDataTree; private _message!: HTMLSpanElement; From f1637084561f595f6c78734141471d41c55665e5 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 10 Jan 2020 08:34:22 +0100 Subject: [PATCH 100/843] Fix issue with breakingOffsetsCount being incorrect at the end --- .../editor/common/viewModel/characterHardWrappingLineMapper.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 6d78f602fd2..4bfd8c100f0 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -197,6 +197,7 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC // Add last segment breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1]; breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1]; + breakingOffsetsCount++; } break; } From 71206c77f1c4c3eb45867a6c50997f2e0a20d6a1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 08:39:30 +0100 Subject: [PATCH 101/843] restore workbench layout/ux state when refactoring is done --- .../bulkEdit/browser/bulkEdit.contribution.ts | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index dffe6a8715b..8f42d383fde 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -15,6 +15,9 @@ import { localize } from 'vs/nls'; import { ViewPaneContainer, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { PaneCompositePanel } from 'vs/workbench/browser/panel'; import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { BulkEditPreviewProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; class BulkEditPreviewContribution { @@ -24,6 +27,7 @@ class BulkEditPreviewContribution { constructor( @IPanelService private _panelService: IPanelService, + @IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService, @IBulkEditService bulkEditService: IBulkEditService, @IContextKeyService contextKeyService: IContextKeyService, ) { @@ -33,6 +37,8 @@ class BulkEditPreviewContribution { private async _previewEdit(edit: WorkspaceEdit) { this._ctxEnabled.set(true); + const oldActivePanel = this._panelService.getActivePanel(); + try { let view: ViewPane | undefined; @@ -50,15 +56,32 @@ class BulkEditPreviewContribution { return { edits: [] }; } - // todo@joh/steVen add view state - // if (this._panelService.getLastActivePanelId() === BulkEditPanel.ID) { - // this._panelService.hideActivePanel(); - // } - return newEditOrUndefined; } finally { + // restore UX state + + // (1) hide refactor panel this._ctxEnabled.set(false); + + // (2) restore previous panel + if (oldActivePanel) { + this._panelService.openPanel(oldActivePanel.getId()); + } else { + this._panelService.hideActivePanel(); + } + + // (3) close preview editors + for (let group of this._editorGroupsService.groups) { + for (let input of group.editors) { + if (!group.isPinned(input) + && input instanceof DiffEditorInput + && input.modifiedInput.getResource()?.scheme === BulkEditPreviewProvider.Schema + ) { + group.closeEditor(input, { preserveFocus: true }); + } + } + } } } } From ec689c65de18e327d1a0dde7c2faa39ffd0cbfc2 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 10 Jan 2020 08:44:47 +0100 Subject: [PATCH 102/843] Remove unnecessary code --- .../characterHardWrappingLineMapper.ts | 55 +------------------ .../common/viewModel/splitLinesCollection.ts | 24 -------- 2 files changed, 1 insertion(+), 78 deletions(-) diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 4bfd8c100f0..35d9381487f 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -315,63 +315,10 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC breakingOffsetsVisibleColumn.length = breakingOffsetsCount; arrPool1 = previousBreakingData.breakOffsets; arrPool2 = previousBreakingData.breakingOffsetsVisibleColumn; - previousBreakingData.breakingColumn = firstLineBreakingColumn; previousBreakingData.breakOffsets = breakingOffsets; previousBreakingData.breakingOffsetsVisibleColumn = breakingOffsetsVisibleColumn; previousBreakingData.wrappedTextIndentLength = wrappedTextIndentLength; return previousBreakingData; - - // return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); - - // const expected = createLineMapping(classifier, lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); - // const actual = new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); - // try { - // actual.assertEqual(expected); - // } catch (err) { - // console.log(`BREAKING!!`); - // console.log(err); - // console.log(` - // firstLineBreakingColumn: ${firstLineBreakingColumn} - - // previous breaks: ${JSON.stringify(prevBreakingOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(prevBreakingOffsetsVisibleColumn)} - // expected breaks: ${JSON.stringify(expected?.breakOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(expected?.breakingOffsetsVisibleColumn)} - // actual breaks: ${JSON.stringify(actual?.breakOffsets)}, breakingOffsetsVisibleColumn: ${JSON.stringify(actual?.breakingOffsetsVisibleColumn)} - - // previous str: ${toAnnotatedText(lineText, previousBreakingData)} - // expected str: ${toAnnotatedText(lineText, expected)} - // actual str: ${toAnnotatedText(lineText, actual)} - - // assertIncrementalLineMapping( - // factory, ${str(lineText)}, 4, - // ${previousBreakingData.breakingColumn}, ${str(toAnnotatedText(lineText, previousBreakingData))}, - // ${expected!.breakingColumn}, ${str(toAnnotatedText(lineText, expected))}, - // WrappingIndent.${hardWrappingIndent === WrappingIndent.None ? 'None' : hardWrappingIndent === WrappingIndent.Same ? 'Same' : hardWrappingIndent === WrappingIndent.Indent ? 'Indent' : 'DeepIndent'} - // ); - // `); - // function str(strr: string) { - // return `'${strr.replace(/\\/g, '\\\\').replace(/'/g, '\\\'')}'`; - // } - // function toAnnotatedText(text: string, lineBreakingData: LineBreakingData | null): string { - // // Insert line break markers again, according to algorithm - // let actualAnnotatedText = ''; - // if (lineBreakingData) { - // let previousLineIndex = 0; - // for (let i = 0, len = text.length; i < len; i++) { - // let r = LineBreakingData.getOutputPositionOfInputOffset(lineBreakingData.breakOffsets, i); - // if (previousLineIndex !== r.outputLineIndex) { - // previousLineIndex = r.outputLineIndex; - // actualAnnotatedText += '|'; - // } - // actualAnnotatedText += text.charAt(i); - // } - // } else { - // // No wrapping - // actualAnnotatedText = text; - // } - // return actualAnnotatedText; - // } - // } - // return actual; } function createLineMapping(classifier: WrappingCharacterClassifier, lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { @@ -459,7 +406,7 @@ function createLineMapping(classifier: WrappingCharacterClassifier, lineText: st breakingOffsets[breakingOffsetsCount] = len; breakingOffsetsVisibleColumn[breakingOffsetsCount] = visibleColumn; - return new LineBreakingData(firstLineBreakingColumn, breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); + return new LineBreakingData(breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); } function computeCharWidth(charCode: number, visibleColumn: number, tabSize: number, columnsForFullWidthChar: number): number { diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 90f3c4c20d8..9f66266bc94 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -28,35 +28,11 @@ export class OutputPosition { export class LineBreakingData { constructor( - public breakingColumn: number, public breakOffsets: number[], public breakingOffsetsVisibleColumn: number[], public wrappedTextIndentLength: number ) { } - assertEqual(other: LineBreakingData | null): void { - if (other === null) { - throw new Error(`x--unexpected--1`); - } - if (other.breakingColumn !== this.breakingColumn) { - throw new Error(`x--unexpected--2`); - } - if (other.wrappedTextIndentLength !== this.wrappedTextIndentLength) { - throw new Error(`x--unexpected--3`); - } - if (other.breakOffsets.length !== this.breakOffsets.length) { - throw new Error(`x--unexpected--4`); - } - for (let i = 0; i < this.breakOffsets.length; i++) { - if (this.breakOffsets[i] !== other.breakOffsets[i]) { - throw new Error(`x--unexpected--5`); - } - if (this.breakingOffsetsVisibleColumn[i] !== other.breakingOffsetsVisibleColumn[i]) { - throw new Error(`x--unexpected--6`); - } - } - } - public static getInputOffsetOfOutputPosition(breakOffsets: number[], outputLineIndex: number, outputOffset: number): number { if (outputLineIndex === 0) { return outputOffset; From afd58aa9b7be44934a067829196d38ba8b20f054 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 10 Jan 2020 08:45:04 +0100 Subject: [PATCH 103/843] Fix compilation --- .../editor/test/common/viewModel/splitLinesCollection.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index 56f06a2a2bc..ba4228ce9a6 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -781,7 +781,7 @@ function createLineMapping(breakingLengths: number[], breakingOffsetsVisibleColu for (let i = 0; i < breakingLengths.length; i++) { sums[i] = (i > 0 ? sums[i - 1] : 0) + breakingLengths[i]; } - return new LineBreakingData(0, sums, breakingOffsetsVisibleColumn, wrappedTextIndentWidth); + return new LineBreakingData(sums, breakingOffsetsVisibleColumn, wrappedTextIndentWidth); } function createModel(text: string): ISimpleModel { From 2dfe5bec96b494593796a75ec3806899d25a720c Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 10 Jan 2020 08:47:11 +0100 Subject: [PATCH 104/843] Fix tests --- .../common/viewModel/characterHardWrappingLineMapper.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts index ef61ca5b183..db130c390b1 100644 --- a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts +++ b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts @@ -44,7 +44,8 @@ function toAnnotatedText(text: string, lineBreakingData: LineBreakingData | null function getLineBreakingData(factory: ILineMapperFactory, tabSize: number, breakAfter: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, text: string, previousLineBreakingData: LineBreakingData | null): LineBreakingData | null { const lineMappingComputer = factory.createLineMappingComputer(tabSize, breakAfter, columnsForFullWidthChar, wrappingIndent); - lineMappingComputer.addRequest(text, previousLineBreakingData); + const previousLineBreakingDataClone = previousLineBreakingData ? new LineBreakingData(previousLineBreakingData.breakOffsets.slice(0), previousLineBreakingData.breakingOffsetsVisibleColumn.slice(0), previousLineBreakingData.wrappedTextIndentLength) : null; + lineMappingComputer.addRequest(text, previousLineBreakingDataClone); return lineMappingComputer.finalize()[0]; } From c9275df30c77078aa6562abe46bc547dc1907cfd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 08:52:22 +0100 Subject: [PATCH 105/843] fix BulkEditPane-ctor --- .../contrib/bulkEdit/browser/bulkEdit.contribution.ts | 2 +- src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index 8f42d383fde..19e206bf2c6 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -9,7 +9,7 @@ import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } fr import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; -import { BulkEditPanel as BulkEditPane } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPane'; +import { BulkEditPane } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPane'; import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewsRegistry } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; import { ViewPaneContainer, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index e4b5a81d91c..11ab40d84e2 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -24,13 +24,14 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; const enum State { Data = 'data', Message = 'message' } -export class BulkEditPanel extends ViewPane { +export class BulkEditPane extends ViewPane { static readonly ID = 'refactorPreview'; @@ -46,6 +47,7 @@ export class BulkEditPanel extends ViewPane { private _currentInput?: BulkFileOperations; constructor( + options: IViewletViewOptions, @IInstantiationService private readonly _instaService: IInstantiationService, @IEditorService private readonly _editorService: IEditorService, @ILabelService private readonly _labelService: ILabelService, @@ -56,7 +58,7 @@ export class BulkEditPanel extends ViewPane { @IContextKeyService contextKeyService: IContextKeyService ) { super( - { id: BulkEditPanel.ID, title: localize('title', "Refactor Preview") }, + options, keybindingService, contextMenuService, configurationService, contextKeyService ); } From 51fc8bf0c33c670791f1082533b8c500c2dc0c96 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 10 Jan 2020 00:04:30 -0800 Subject: [PATCH 106/843] Initial approach to search editor UI. --- .../search/browser/media/searchEditor.css | 165 ++++++++ .../search/browser/patternInputWidget.ts | 3 +- .../search/browser/search.contribution.ts | 33 +- .../contrib/search/browser/searchActions.ts | 31 +- .../contrib/search/browser/searchEditor.ts | 399 +++++++++++++++++- .../contrib/search/browser/searchView.ts | 2 +- .../contrib/search/browser/searchWidget.ts | 52 ++- .../contrib/search/common/constants.ts | 1 + .../services/search/common/search.ts | 1 + 9 files changed, 656 insertions(+), 31 deletions(-) create mode 100644 src/vs/workbench/contrib/search/browser/media/searchEditor.css diff --git a/src/vs/workbench/contrib/search/browser/media/searchEditor.css b/src/vs/workbench/contrib/search/browser/media/searchEditor.css new file mode 100644 index 00000000000..c12e7737acc --- /dev/null +++ b/src/vs/workbench/contrib/search/browser/media/searchEditor.css @@ -0,0 +1,165 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.search-editor { + display: flex; + flex-direction: column; +} + +.search-editor .search-results { + flex: 1; +} + +.search-editor .query-container { + margin: 0px 12px 12px 2px; + padding-top: 6px; +} + +.search-editor .search-widget .toggle-replace-button { + position: absolute; + top: 0; + left: 0; + width: 16px; + height: 100%; + box-sizing: border-box; + background-position: center center; + background-repeat: no-repeat; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; +} + +.search-editor .search-widget .search-container, +.search-editor .search-widget .replace-container { + margin-left: 18px; + display: flex; + align-items: center; +} + +.search-editor .search-widget .monaco-findInput { + display: inline-block; + vertical-align: middle; + width: 100%; +} + +.search-editor .search-widget .monaco-inputbox > .wrapper { + height: 100%; +} + +.search-editor .search-widget .monaco-inputbox > .wrapper > .mirror, +.search-editor .search-widget .monaco-inputbox > .wrapper > textarea.input { + padding: 3px; + padding-left: 4px; +} + +.search-editor .search-widget .monaco-inputbox > .wrapper > .mirror { + max-height: 134px; +} + +/* NOTE: height is also used in searchWidget.ts as a constant*/ +.search-editor .search-widget .monaco-inputbox > .wrapper > textarea.input { + overflow: initial; + height: 24px; /* set initial height before measure */ +} + +.search-editor .monaco-inputbox > .wrapper > textarea.input { + scrollbar-width: none; /* Firefox: hide scrollbar */ +} + +.search-editor .monaco-inputbox > .wrapper > textarea.input::-webkit-scrollbar { + display: none; +} + +.search-editor .search-widget .context-lines-input { + display: none; +} + +.search-editor .search-widget.show-context .context-lines-input { + display: inherit; + margin-left: 5px; + margin-right: 2px; + max-width: 50px; +} + + +.search-editor .search-widget .replace-container { + margin-top: 6px; + position: relative; + display: inline-flex; +} + +.search-editor .search-widget .replace-input { + position: relative; + display: flex; + vertical-align: middle; + width: auto !important; + height: 25px; +} + +.search-editor .search-widget .replace-input > .controls { + position: absolute; + top: 3px; + right: 2px; +} + +.search-editor .search-widget .replace-container.disabled { + display: none; +} + +.search-editor .search-widget .replace-container .monaco-action-bar { + margin-left: 0; +} + +.search-editor .search-widget .replace-container .monaco-action-bar { + height: 25px; +} + +.search-editor .search-widget .replace-container .monaco-action-bar .action-item .codicon { + background-repeat: no-repeat; + width: 25px; + height: 25px; + margin-right: 0; + display: flex; + align-items: center; + justify-content: center; +} + +.search-editor .includes-excludes { + min-height: 1em; + position: relative; + margin: 0 0 0 17px; +} + +.search-editor .includes-excludes .expand { + position: absolute; + right: -2px; + cursor: pointer; + width: 25px; + height: 16px; + z-index: 2; /* Force it above the search results message, which has a negative top margin */ +} + +.search-editor .includes-excludes .file-types { + display: none; +} + +.search-editor .includes-excludes.expanded .file-types { + display: inherit; +} + +.search-editor .includes-excludes.expanded .file-types:last-child { + padding-bottom: 10px; +} + +.search-editor .includes-excludes.expanded h4 { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + padding: 4px 0 0; + margin: 0; + font-size: 11px; + font-weight: normal; +} diff --git a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts index 8b34c22cea7..726ce4ee0b2 100644 --- a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts @@ -10,7 +10,7 @@ import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { IInputValidator, HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { KeyCode } from 'vs/base/common/keyCodes'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler'; @@ -170,6 +170,7 @@ export class PatternInputWidget extends Widget { case KeyCode.Escape: this._onCancel.fire(); return; + case KeyCode.Tab: case KeyCode.Tab | KeyMod.Shift: return; default: if (this.searchConfig.searchOnType) { this._onCancel.fire(); diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 6a83637408e..ab59270e380 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -39,7 +39,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, RerunEditorSearchAction, RerunEditorSearchWithContextAction, ExpandAllAction } 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, RerunEditorSearchWithContextAction, ExpandAllAction, OpenSearchEditorAction } 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 { registerContributions as searchWidgetContributions } from 'vs/workbench/contrib/search/browser/searchWidget'; @@ -56,6 +56,9 @@ import { ExplorerViewPaneContainer } from 'vs/workbench/contrib/files/browser/ex import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { assertType } from 'vs/base/common/types'; import { SearchViewPaneContainer } from 'vs/workbench/contrib/search/browser/searchViewlet'; +import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; +import { SearchEditor, SearchEditorInput } from 'vs/workbench/contrib/search/browser/searchEditor'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); registerSingleton(ISearchHistoryService, SearchHistoryService, true); @@ -647,6 +650,11 @@ registry.registerWorkbenchAction( 'Search: Open Results in Editor', category, ContextKeyExpr.and(Constants.EnableSearchEditorPreview)); +registry.registerWorkbenchAction( + SyncActionDescriptor.create(OpenSearchEditorAction, OpenSearchEditorAction.ID, OpenSearchEditorAction.LABEL), + 'Search: Open new Search Editor', category, + ContextKeyExpr.and(Constants.EnableSearchEditorPreview)); + const searchEditorCategory = nls.localize({ comment: ['The name of the tabbed search view'], key: 'searcheditor' }, "Search Editor"); registry.registerWorkbenchAction( SyncActionDescriptor.create(RerunEditorSearchAction, RerunEditorSearchAction.ID, RerunEditorSearchAction.LABEL, @@ -828,6 +836,17 @@ configurationRegistry.registerConfiguration({ default: false, description: nls.localize('search.enableSearchEditorPreview', "Experimental: When enabled, allows opening workspace search results in an editor.") }, + 'search.searchEditorPreview.doubleClickBehaviour': { + type: 'string', + enum: ['selectWord', 'goToLocation', 'openLocationToSide'], + default: 'goToLocation', + enumDescriptions: [ + nls.localize('search.searchEditorPreview.doubleClickBehaviour.selectWord', "Double clicking selects the word under the cursor."), + nls.localize('search.searchEditorPreview.doubleClickBehaviour.goToLocation', "Double clicking opens the result in the active editor group."), + nls.localize('search.searchEditorPreview.doubleClickBehaviour.openLocationToSide', "Double clicking opens the result in the editor group to the side, creating one if it does not yet exist."), + ], + markdownDescription: nls.localize('search.searchEditorPreview.doubleClickBehaviour', "Configure effect of double clicking a result in a Search Editor.\n\n `#search.enableSearchEditorPreview#` must be enabled for this setting to have an effect.") + }, 'search.sortOrder': { 'type': 'string', 'enum': [SearchSortOrder.Default, SearchSortOrder.FileNames, SearchSortOrder.Type, SearchSortOrder.Modified, SearchSortOrder.CountDescending, SearchSortOrder.CountAscending], @@ -872,3 +891,15 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { }, order: 2 }); + + +Registry.as(EditorExtensions.Editors).registerEditor( + EditorDescriptor.create( + SearchEditor, + SearchEditor.ID, + nls.localize('defaultPreferencesEditor', "Search Editor") + ), + [ + new SyncDescriptor(SearchEditorInput) + ] +); diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 785f9f45755..3be9ac82737 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -29,7 +29,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { SearchViewPaneContainer } 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, refreshActiveEditorSearch } from 'vs/workbench/contrib/search/browser/searchEditor'; +import { createEditorFromSearchResult, refreshActiveEditorSearch, openNewSearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; @@ -508,6 +508,30 @@ export class CancelSearchAction extends Action { } } +export class OpenSearchEditorAction extends Action { + + static readonly ID: string = Constants.OpenNewEditorCommandId; + static readonly LABEL = nls.localize('search.openNewEditor', "Open new Search Editor"); + + constructor(id: string, label: string, + @IEditorService private editorService: IEditorService, + @IConfigurationService private configurationService: IConfigurationService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { + super(id, label); + } + + get enabled(): boolean { + return true; + } + + async run() { + if (this.configurationService.getValue('search').enableSearchEditorPreview) { + await openNewSearchEditor(this.editorService, this.instantiationService); + } + } +} + export class OpenResultsInEditorAction extends Action { static readonly ID: string = Constants.OpenInEditorCommandId; @@ -518,7 +542,8 @@ export class OpenResultsInEditorAction extends Action { @IPanelService private panelService: IPanelService, @ILabelService private labelService: ILabelService, @IEditorService private editorService: IEditorService, - @IConfigurationService private configurationService: IConfigurationService + @IConfigurationService private configurationService: IConfigurationService, + @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(id, label, 'codicon-go-to-file'); } @@ -535,7 +560,7 @@ export class OpenResultsInEditorAction extends Action { 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); + await createEditorFromSearchResult(searchView.searchResult, searchView.searchIncludePattern.getValue(), searchView.searchExcludePattern.getValue(), this.labelService, this.editorService, this.instantiationService); } } } diff --git a/src/vs/workbench/contrib/search/browser/searchEditor.ts b/src/vs/workbench/contrib/search/browser/searchEditor.ts index fda96298247..83b746109a5 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditor.ts @@ -3,26 +3,356 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -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 * as DOM from 'vs/base/browser/dom'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; 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, IPatternInfo, ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as network from 'vs/base/common/network'; +import { repeat } from 'vs/base/common/strings'; +import { assertIsDefined } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import 'vs/css!./media/searchEditor'; +import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import type { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; -import { ITextModel, TrackedRangeStickiness, EndOfLinePreference } from 'vs/editor/common/model'; +import { EndOfLinePreference, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { localize } from 'vs/nls'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { searchEditorFindMatch, searchEditorFindMatchBorder } from 'vs/platform/theme/common/colorRegistry'; +import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; +import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput'; +import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget'; +import { SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget'; 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'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { searchEditorFindMatch, searchEditorFindMatchBorder } from 'vs/platform/theme/common/colorRegistry'; -import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput'; -import { localize } from 'vs/nls'; -import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { FileMatch, Match, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IPatternInfo, ISearchConfigurationProperties, ITextQuery } from 'vs/workbench/services/search/common/search'; +import { Delayer } from 'vs/base/common/async'; + +const RESULT_LINE_REGEX = /^(\s+)(\d+)(:| )(\s+)(.*)$/; + +type SearchConfiguration = { + query: string, + includes: string, + excludes: string, + contextLines: number, + wholeWord: boolean, + caseSensitive: boolean, + regexp: boolean, + useIgnores: boolean +}; + +let searchEditorInputInstances = 0; +export class SearchEditorInput extends EditorInput { + static readonly ID: string = 'workbench.editorinputs.searchEditorInput'; + + public config: SearchConfiguration; + private instanceNumber: number = searchEditorInputInstances++; + + constructor( + config: SearchConfiguration | undefined, + @IModelService private readonly modelService: IModelService, + @IModeService private readonly modeService: IModeService, + ) { + super(); + + if (config === undefined) { + this.config = { query: '', includes: '', excludes: '', contextLines: 0, wholeWord: false, caseSensitive: false, regexp: false, useIgnores: true }; + } else { + this.config = config; + } + + const searchResultMode = this.modeService.create('search-result'); + this.modelService.createModel('', searchResultMode, this.getResource()); + } + + getTypeId(): string { + return SearchEditorInput.ID; + } + + getResource(): URI { + return URI.from({ scheme: 'code-search', fragment: `${this.instanceNumber}` }); + } + + getName(): string { + return this.config.query ? localize('searchTitle.withQuery', "Search: {0}", this.config.query) : localize('searchTitle', "Search"); + } + + setConfig(config: SearchConfiguration) { + this.config = config; + this._onDidChangeLabel.fire(); + } + + async resolve() { + return null; + } + + dispose() { + this.modelService.destroyModel(this.getResource()); + super.dispose(); + } +} + +export class SearchEditor extends BaseEditor { + static readonly ID: string = 'workbench.editor.searchEditor'; + + private queryEditorWidget!: SearchWidget; + private searchResultEditor!: CodeEditorWidget; + private queryEditorContainer!: HTMLElement; + private dimension?: DOM.Dimension; + private inputPatternIncludes!: PatternInputWidget; + private inputPatternExcludes!: ExcludePatternInputWidget; + private includesExcludesContainer!: HTMLElement; + private toggleQueryDetailsButton!: HTMLElement; + + private runSearchDelayer = new Delayer(300); + private pauseSearching: boolean = false; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IStorageService storageService: IStorageService, + @IModelService private readonly modelService: IModelService, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @ILabelService private readonly labelService: ILabelService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IContextViewService private readonly contextViewService: IContextViewService, + @ICommandService private readonly commandService: ICommandService, + ) { + super(SearchEditor.ID, telemetryService, themeService, storageService); + } + + createEditor(parent: HTMLElement) { + DOM.addClass(parent, 'search-editor'); + + // Query + this.queryEditorContainer = DOM.append(parent, DOM.$('.query-container')); + + this.queryEditorWidget = this._register(this.instantiationService.createInstance(SearchWidget, this.queryEditorContainer, { _hideReplaceToggle: true, showContextToggle: true })); + this._register(this.queryEditorWidget.onReplaceToggled(() => this.reLayout())); + this._register(this.queryEditorWidget.onDidHeightChange(() => this.reLayout())); + this.queryEditorWidget.onSearchSubmit(() => this.runSearch()); + this.queryEditorWidget.searchInput.onDidOptionChange(() => this.runSearch()); + this.queryEditorWidget.onDidToggleContext(() => this.runSearch()); + + // Includes/Excludes Dropdown + this.includesExcludesContainer = DOM.append(this.queryEditorContainer, DOM.$('.includes-excludes')); + // // Toggle query details button + this.toggleQueryDetailsButton = DOM.append(this.includesExcludesContainer, DOM.$('.expand.codicon.codicon-ellipsis', { tabindex: 0, role: 'button', title: localize('moreSearch', "Toggle Search Details") })); + this._register(DOM.addDisposableListener(this.toggleQueryDetailsButton, DOM.EventType.CLICK, e => { + DOM.EventHelper.stop(e); + this.toggleQueryDetails(); + })); + this._register(DOM.addDisposableListener(this.toggleQueryDetailsButton, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { + const event = new StandardKeyboardEvent(e); + + if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { + DOM.EventHelper.stop(e); + this.toggleQueryDetails(); + } + })); + this._register(DOM.addDisposableListener(this.toggleQueryDetailsButton, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + const event = new StandardKeyboardEvent(e); + + if (event.equals(KeyMod.Shift | KeyCode.Tab)) { + if (this.queryEditorWidget.isReplaceActive()) { + this.queryEditorWidget.focusReplaceAllAction(); + } else { + this.queryEditorWidget.isReplaceShown() ? this.queryEditorWidget.replaceInput.focusOnPreserve() : this.queryEditorWidget.focusRegexAction(); + } + DOM.EventHelper.stop(e); + } + })); + + // // Includes + + const folderIncludesList = DOM.append(this.includesExcludesContainer, DOM.$('.file-types.includes')); + const filesToIncludeTitle = localize('searchScope.includes', "files to include"); + DOM.append(folderIncludesList, DOM.$('h4', undefined, filesToIncludeTitle)); + + this.inputPatternIncludes = this._register(this.instantiationService.createInstance(PatternInputWidget, folderIncludesList, this.contextViewService, { + ariaLabel: localize('label.includes', 'Search Include Patterns'), + })); + this.inputPatternIncludes.onSubmit(_triggeredOnType => this.runSearch()); + + // // Excludes + const excludesList = DOM.append(this.includesExcludesContainer, DOM.$('.file-types.excludes')); + const excludesTitle = localize('searchScope.excludes', "files to exclude"); + DOM.append(excludesList, DOM.$('h4', undefined, excludesTitle)); + + this.inputPatternExcludes = this._register(this.instantiationService.createInstance(ExcludePatternInputWidget, excludesList, this.contextViewService, { + ariaLabel: localize('label.excludes', 'Search Exclude Patterns'), + })); + + this.inputPatternExcludes.onSubmit(_triggeredOnType => this.runSearch()); + this.inputPatternExcludes.onChangeIgnoreBox(() => this.runSearch()); + + // Editor + const searchResultContainer = DOM.append(parent, DOM.$('.search-results')); + const configuration: IEditorOptions = this.configurationService.getValue('editor', { overrideIdentifier: 'search-result' }); + const options: ICodeEditorWidgetOptions = {}; + this.searchResultEditor = this._register(this.instantiationService.createInstance(CodeEditorWidget, searchResultContainer, configuration, options)); + this.searchResultEditor.onMouseUp(e => { + + if (e.event.detail === 2) { + const behaviour = this.configurationService.getValue('search').searchEditorPreview.doubleClickBehaviour; + const position = e.target.position; + if (position && behaviour !== 'selectWord') { + const line = this.searchResultEditor.getModel()?.getLineContent(position.lineNumber) ?? ''; + if (line.match(RESULT_LINE_REGEX)) { + this.searchResultEditor.setSelection(Range.fromPositions(position)); + this.commandService.executeCommand(behaviour === 'goToLocation' ? 'editor.action.goToDeclaration' : 'editor.action.openDeclarationToTheSide'); + } + } + } + }); + } + + private async runSearch() { + if (!this.pauseSearching) { + this.runSearchDelayer.trigger(() => this.doRunSearch()); + } + } + + private async doRunSearch() { + const startInput = this.input; + + const config: SearchConfiguration = { + caseSensitive: this.queryEditorWidget.searchInput.getCaseSensitive(), + contextLines: this.queryEditorWidget.contextLines(), + excludes: this.inputPatternExcludes.getValue(), + includes: this.inputPatternIncludes.getValue(), + query: this.queryEditorWidget.searchInput.getValue(), + regexp: this.queryEditorWidget.searchInput.getRegex(), + wholeWord: this.queryEditorWidget.searchInput.getWholeWords(), + useIgnores: this.inputPatternExcludes.useExcludesAndIgnoreFiles() + }; + + const content: IPatternInfo = { + pattern: config.query, + isRegExp: config.regexp, + isCaseSensitive: config.caseSensitive, + isWordMatch: config.wholeWord, + }; + + const options: ITextQueryBuilderOptions = { + _reason: 'searchEditor', + extraFileResources: this.instantiationService.invokeFunction(getOutOfWorkspaceEditorResources), + maxResults: 10000, + disregardIgnoreFiles: !config.useIgnores, + disregardExcludeSettings: !config.useIgnores, + excludePattern: config.excludes, + includePattern: config.includes, + previewOptions: { + matchLines: 1, + charsPerLine: 1000 + }, + afterContext: config.contextLines, + beforeContext: config.contextLines, + isSmartCase: this.configurationService.getValue('search').smartCase, + expandPatterns: true + }; + + const folderResources = this.contextService.getWorkspace().folders; + let query: ITextQuery; + try { + const queryBuilder = this.instantiationService.createInstance(QueryBuilder); + query = queryBuilder.text(content, folderResources.map(folder => folder.uri), options); + } + catch (err) { + return; + } + const searchModel = this.instantiationService.createInstance(SearchModel); + await searchModel.search(query); + if (this.input !== startInput) { + searchModel.dispose(); + return; + } + + (assertIsDefined(this._input) as SearchEditorInput).setConfig(config); + + const labelFormatter = (uri: URI): string => this.labelService.getUriLabel(uri, { relative: true }); + const results = serializeSearchResultForEditor(searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, false); + const textModel = assertIsDefined(this.searchResultEditor.getModel()); + textModel.setValue(results.text.join(lineDelimiter)); + textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); + + searchModel.dispose(); + } + + layout(dimension: DOM.Dimension) { + this.dimension = dimension; + this.reLayout(); + } + + focusInput() { + this.queryEditorWidget.focus(); + } + + private reLayout() { + if (this.dimension) { + this.queryEditorWidget.setWidth(this.dimension.width - 28 /* container margin */); + this.searchResultEditor.layout({ height: this.dimension.height - DOM.getTotalHeight(this.queryEditorContainer), width: this.dimension.width }); + this.inputPatternExcludes.setWidth(this.dimension.width - 28 /* container margin */); + this.inputPatternIncludes.setWidth(this.dimension.width - 28 /* container margin */); + } + } + + async setInput(newInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + if (!(newInput instanceof SearchEditorInput)) { return; } + this.pauseSearching = true; + // TODO: Manage model lifecycle in SearchEditorInput + const model = this.modelService.getModel(newInput.getResource()); + + this.searchResultEditor.setModel(model); + this.queryEditorWidget.setValue(newInput.config.query, true); + this.queryEditorWidget.searchInput.setCaseSensitive(newInput.config.caseSensitive); + this.queryEditorWidget.searchInput.setRegex(newInput.config.regexp); + this.queryEditorWidget.searchInput.setWholeWords(newInput.config.wholeWord); + this.queryEditorWidget.setContextLines(newInput.config.contextLines); + this.inputPatternExcludes.setValue(newInput.config.excludes); + this.inputPatternIncludes.setValue(newInput.config.includes); + this.inputPatternExcludes.setUseExcludesAndIgnoreFiles(newInput.config.useIgnores); + + this.focusInput(); + await super.setInput(newInput, options, token); + this.pauseSearching = false; + } + + toggleQueryDetails(): void { + const cls = 'expanded'; + const shouldShow = !DOM.hasClass(this.includesExcludesContainer, cls); + + if (shouldShow) { + this.toggleQueryDetailsButton.setAttribute('aria-expanded', 'true'); + DOM.addClass(this.includesExcludesContainer, cls); + } else { + this.toggleQueryDetailsButton.setAttribute('aria-expanded', 'false'); + DOM.removeClass(this.includesExcludesContainer, cls); + } + + this.reLayout(); + } + + getModel() { + return this.searchResultEditor.getModel(); + } +} // Using \r\n on Windows inserts an extra newline between results. const lineDelimiter = '\n'; @@ -221,8 +551,8 @@ const searchHeaderToContentPattern = (header: string[]): SearchHeader => { return query; }; -const serializeSearchResultForEditor = (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, contextLines: number, labelFormatter: (x: URI) => string): SearchResultSerialization => { - const header = contentPatternToSearchResultHeader(searchResult.query, rawIncludePattern, rawExcludePattern, contextLines); +const serializeSearchResultForEditor = (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, contextLines: number, labelFormatter: (x: URI) => string, includeHeader: boolean): SearchResultSerialization => { + const header = includeHeader ? contentPatternToSearchResultHeader(searchResult.query, rawIncludePattern, rawExcludePattern, contextLines) : []; const allResults = flattenSearchResultSerializations( flatten( @@ -290,20 +620,29 @@ export const refreshActiveEditorSearch = await searchModel.search(query); const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true }); - const results = serializeSearchResultForEditor(searchModel.searchResult, contentPattern.includes, contentPattern.excludes, contextLines, labelFormatter); + const results = serializeSearchResultForEditor(searchModel.searchResult, contentPattern.includes, contentPattern.excludes, contextLines, labelFormatter, false); textModel.setValue(results.text.join(lineDelimiter)); textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); }; +export const openNewSearchEditor = + async (editorService: IEditorService, instantiationService: IInstantiationService) => { + await editorService.openEditor(instantiationService.createInstance(SearchEditorInput, undefined), { pinned: true }); + }; export const createEditorFromSearchResult = - async (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, labelService: ILabelService, editorService: IEditorService) => { - const searchTerm = searchResult.query?.contentPattern.pattern.replace(/[^\w-_. ]/g, '') || 'Search'; + async (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, labelService: ILabelService, editorService: IEditorService, instantiationService: IInstantiationService) => { + if (!searchResult.query) { + console.error('Expected searchResult.query to be defined. Got', searchResult); + return; + } + + 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, 0, labelFormatter); + const results = serializeSearchResultForEditor(searchResult, rawIncludePattern, rawExcludePattern, 0, labelFormatter, false); const contents = results.text.join(lineDelimiter); let possible = { contents, @@ -312,6 +651,7 @@ export const createEditorFromSearchResult = }; let id = 0; + let existing = editorService.getOpened(possible); while (existing) { if (existing instanceof UntitledTextEditorInput) { @@ -325,10 +665,23 @@ export const createEditorFromSearchResult = existing = editorService.getOpened(possible); } - const editor = await editorService.openEditor(possible); - const control = editor?.getControl()!; - const model = control.getModel() as ITextModel; + const editor = await editorService.openEditor( + instantiationService.createInstance( + SearchEditorInput, + { + query: searchResult.query.contentPattern.pattern, + regexp: !!searchResult.query.contentPattern.isRegExp, + caseSensitive: !!searchResult.query.contentPattern.isCaseSensitive, + wholeWord: !!searchResult.query.contentPattern.isWordMatch, + includes: rawIncludePattern, + excludes: rawExcludePattern, + contextLines: 0, + useIgnores: !searchResult.query.userDisabledExcludesAndIgnoreFiles, + }), + { pinned: true }) as SearchEditor; + const model = assertIsDefined(editor.getModel()); + model.setValue(contents); model.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); }; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 527fd051a1e..16ba94358ca 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1559,7 +1559,7 @@ export class SearchView extends ViewPane { 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); + createEditorFromSearchResult(this.searchResult, this.searchIncludePattern.getValue(), this.searchExcludePattern.getValue(), this.labelService, this.editorService, this.instantiationService); })); } else { diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 77defd9110c..402f4313f1a 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -9,7 +9,7 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button, IButtonOptions } from 'vs/base/browser/ui/button/button'; import { FindInput, IFindInputOptions } from 'vs/base/browser/ui/findinput/findInput'; import { ReplaceInput } from 'vs/base/browser/ui/findinput/replaceInput'; -import { IMessage } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IMessage, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { Widget } from 'vs/base/browser/ui/widget'; import { Action } from 'vs/base/common/actions'; import { Delayer } from 'vs/base/common/async'; @@ -34,6 +34,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { isMacintosh } from 'vs/base/common/platform'; +import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; /** Specified in searchview.css */ export const SingleLineInputHeight = 24; @@ -47,6 +48,8 @@ export interface ISearchWidgetOptions { searchHistory?: string[]; replaceHistory?: string[]; preserveCase?: boolean; + _hideReplaceToggle?: boolean; // TODO: Search Editor's replace experience + showContextToggle?: boolean; } class ReplaceAllAction extends Action { @@ -151,7 +154,13 @@ export class SearchWidget extends Widget { private _onDidHeightChange = this._register(new Emitter()); readonly onDidHeightChange: Event = this._onDidHeightChange.event; + private readonly _onDidToggleContext = new Emitter(); + readonly onDidToggleContext: Event = this._onDidToggleContext.event; + private temporarilySkipSearchOnChange = false; + private showContextCheckbox!: Checkbox; + private contextLinesInput!: InputBox; + private _contextLineInputDelayer: Delayer; constructor( container: HTMLElement, @@ -168,7 +177,10 @@ export class SearchWidget extends Widget { this.replaceActive = Constants.ReplaceActiveKey.bindTo(this.contextKeyService); this.searchInputBoxFocused = Constants.SearchInputBoxFocusedKey.bindTo(this.contextKeyService); this.replaceInputBoxFocused = Constants.ReplaceInputBoxFocusedKey.bindTo(this.contextKeyService); + this._replaceHistoryDelayer = new Delayer(500); + this._contextLineInputDelayer = new Delayer(300); + this._searchDelayer = this._register(new Delayer(this.searchConfiguration.searchOnTypeDebouncePeriod)); this.render(container, options); @@ -275,7 +287,9 @@ export class SearchWidget extends Widget { this.domNode = dom.append(container, dom.$('.search-widget')); this.domNode.style.position = 'relative'; - this.renderToggleReplaceButton(this.domNode); + if (!options._hideReplaceToggle) { + this.renderToggleReplaceButton(this.domNode); + } this.renderSearchInput(this.domNode, options); this.renderReplaceInput(this.domNode, options); @@ -356,6 +370,36 @@ export class SearchWidget extends Widget { this.ignoreGlobalFindBufferOnNextFocus = false; })); this._register(this.searchInputFocusTracker.onDidBlur(() => this.searchInputBoxFocused.set(false))); + + + this.showContextCheckbox = new Checkbox({ isChecked: false, title: nls.localize('showContext', "Show Context"), actionClassName: 'codicon-list-selection' }); + this._register(this.showContextCheckbox.onChange(() => { + dom.toggleClass(parent, 'show-context', this.showContextCheckbox.checked); + this._onDidToggleContext.fire(); + })); + + if (options.showContextToggle) { + this.contextLinesInput = new InputBox(searchInputContainer, this.contextViewService, { type: 'number' }); + dom.addClass(this.contextLinesInput.element, 'context-lines-input'); + this.contextLinesInput.value = '2'; + this._register(this.contextLinesInput.onDidChange(() => { + if (this.contextLinesInput.value.includes('-')) { + this.contextLinesInput.value = '0'; + } + this._contextLineInputDelayer.trigger(() => this._onDidToggleContext.fire()); + })); + dom.append(searchInputContainer, this.showContextCheckbox.domNode); + } + } + + public setContextLines(lines: number) { + if (!this.contextLinesInput) { return; } + if (lines === 0) { + this.showContextCheckbox.checked = false; + } else { + this.showContextCheckbox.checked = true; + this.contextLinesInput.value = '' + lines; + } } private renderReplaceInput(parent: HTMLElement, options: ISearchWidgetOptions): void { @@ -580,6 +624,10 @@ export class SearchWidget extends Widget { this._onSearchSubmit.fire(triggeredOnType); } + contextLines() { + return this.showContextCheckbox.checked ? +this.contextLinesInput.value : 0; + } + dispose(): void { this.setReplaceAllActionState(false); super.dispose(); diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index b7c72d5d5cd..018bc7c0222 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 OpenNewEditorCommandId = 'search.action.openNewEditor'; export const RerunEditorSearchCommandId = 'search.action.rerunEditorSearch'; export const RerunEditorSearchWithContextCommandId = 'search.action.rerunEditorSearchWithContext'; export const ClearSearchHistoryCommandId = 'search.action.clearHistory'; diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 21e77c7f90f..99ba52ad15f 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -335,6 +335,7 @@ export interface ISearchConfigurationProperties { searchOnType: boolean; searchOnTypeDebouncePeriod: number; enableSearchEditorPreview: boolean; + searchEditorPreview: { doubleClickBehaviour: 'selectWord' | 'goToLocation' | 'openLocationToSide' }; searchEditorPreviewForceAbsolutePaths: boolean; sortOrder: SearchSortOrder; } From 0e57fa880a8a7c03b08a02f33902fe5c5d2991c1 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 10 Jan 2020 09:31:22 +0100 Subject: [PATCH 107/843] Renames --- .../editor/browser/widget/codeEditorWidget.ts | 4 +- ...pper.ts => monospaceLineBreaksComputer.ts} | 74 ++++---- .../common/viewModel/splitLinesCollection.ts | 122 ++++++------- .../editor/common/viewModel/viewModelImpl.ts | 26 +-- .../contrib/comment/lineCommentCommand.ts | 1 - .../test/browser/commands/sideEditing.test.ts | 4 +- .../test/browser/controller/cursor.test.ts | 16 +- .../controller/cursorMoveCommand.test.ts | 4 +- ...ts => monospaceLineBreaksComputer.test.ts} | 170 +++++++++--------- .../viewModel/splitLinesCollection.test.ts | 16 +- .../test/common/viewModel/testViewModel.ts | 4 +- 11 files changed, 220 insertions(+), 221 deletions(-) rename src/vs/editor/common/viewModel/{characterHardWrappingLineMapper.ts => monospaceLineBreaksComputer.ts} (81%) rename src/vs/editor/test/common/viewModel/{characterHardWrappingLineMapper.test.ts => monospaceLineBreaksComputer.test.ts} (50%) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 00d0d4ad3d5..ddfaa481492 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -50,7 +50,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; +import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; let EDITOR_ID = 0; @@ -1330,7 +1330,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE model.onBeforeAttached(); - const viewModel = new ViewModel(this._id, this._configuration, model, CharacterHardWrappingLineMapperFactory.create(this._configuration.options), (callback) => dom.scheduleAtNextAnimationFrame(callback)); + const viewModel = new ViewModel(this._id, this._configuration, model, MonospaceLineBreaksComputerFactory.create(this._configuration.options), (callback) => dom.scheduleAtNextAnimationFrame(callback)); listenersToRemove.push(model.onDidChangeDecorations((e) => this._onDidChangeModelDecorations.fire(e))); listenersToRemove.push(model.onDidChangeLanguage((e) => { diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts similarity index 81% rename from src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts rename to src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts index 35d9381487f..be52ca2265a 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts @@ -7,7 +7,7 @@ import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { WrappingIndent, IComputedEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; -import { ILineMapperFactory, LineBreakingData, ILineMappingComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { ILineBreaksComputerFactory, LineBreakData, ILineBreaksComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; const enum CharacterClass { NONE = 0, @@ -51,10 +51,10 @@ class WrappingCharacterClassifier extends CharacterClassifier { } } -export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactory { +export class MonospaceLineBreaksComputerFactory implements ILineBreaksComputerFactory { - public static create(options: IComputedEditorOptions): CharacterHardWrappingLineMapperFactory { - return new CharacterHardWrappingLineMapperFactory( + public static create(options: IComputedEditorOptions): MonospaceLineBreaksComputerFactory { + return new MonospaceLineBreaksComputerFactory( options.get(EditorOption.wordWrapBreakBeforeCharacters), options.get(EditorOption.wordWrapBreakAfterCharacters) ); @@ -66,26 +66,26 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor this.classifier = new WrappingCharacterClassifier(breakBeforeChars, breakAfterChars); } - public createLineMappingComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineMappingComputer { + public createLineBreaksComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineBreaksComputer { tabSize = tabSize | 0; //@perf wrappingColumn = +wrappingColumn; //@perf columnsForFullWidthChar = +columnsForFullWidthChar; //@perf let requests: string[] = []; - let previousBreakingData: (LineBreakingData | null)[] = []; + let previousBreakingData: (LineBreakData | null)[] = []; return { - addRequest: (lineText: string, previousLineBreakingData: LineBreakingData | null) => { + addRequest: (lineText: string, previousLineBreakData: LineBreakData | null) => { requests.push(lineText); - previousBreakingData.push(previousLineBreakingData); + previousBreakingData.push(previousLineBreakData); }, finalize: () => { - let result: (LineBreakingData | null)[] = []; + let result: (LineBreakData | null)[] = []; for (let i = 0, len = requests.length; i < len; i++) { - const previousLineBreakingData = previousBreakingData[i]; - if (previousLineBreakingData) { - result[i] = createLineMappingFromPreviousLineMapping(this.classifier, previousLineBreakingData, requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + const previousLineBreakData = previousBreakingData[i]; + if (previousLineBreakData) { + result[i] = createLineBreaksFromPreviousLineBreaks(this.classifier, previousLineBreakData, requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); } else { - result[i] = createLineMapping(this.classifier, requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + result[i] = createLineBreaks(this.classifier, requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); } } return result; @@ -96,8 +96,8 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor let arrPool1: number[] = []; let arrPool2: number[] = []; -function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterClassifier, previousBreakingData: LineBreakingData, lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { - if (firstLineBreakingColumn === -1) { +function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterClassifier, previousBreakingData: LineBreakData, lineText: string, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): LineBreakData | null { + if (firstLineBreakColumn === -1) { return null; } @@ -107,16 +107,16 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC } const prevBreakingOffsets = previousBreakingData.breakOffsets; - const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakingOffsetsVisibleColumn; + const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakOffsetsVisibleColumn; - const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); - const wrappedLineBreakingColumn = firstLineBreakingColumn - wrappedTextIndentLength; + const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakColumn, columnsForFullWidthChar, wrappingIndent); + const wrappedLineBreakColumn = firstLineBreakColumn - wrappedTextIndentLength; let breakingOffsets: number[] = arrPool1; let breakingOffsetsVisibleColumn: number[] = arrPool2; let breakingOffsetsCount: number = 0; - let breakingColumn = firstLineBreakingColumn; + let breakingColumn = firstLineBreakColumn; const prevLen = prevBreakingOffsets.length; let prevIndex = 0; @@ -178,7 +178,7 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC forcedBreakOffset = charStartOffset; forcedBreakOffsetVisibleColumn = visibleColumn - charWidth; - if (visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { + if (visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakColumn) { // Cannot break at `breakOffset` => reset it if it was set breakOffset = 0; } @@ -238,7 +238,7 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC forcedBreakOffsetVisibleColumn = visibleColumn; } - if (visibleColumn <= breakingColumn - wrappedLineBreakingColumn) { + if (visibleColumn <= breakingColumn - wrappedLineBreakColumn) { // went too far! break; } @@ -256,7 +256,7 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC } if (breakOffset !== 0) { - const remainingWidthOfNextLine = wrappedLineBreakingColumn - (forcedBreakOffsetVisibleColumn - breakOffsetVisibleColumn); + const remainingWidthOfNextLine = wrappedLineBreakColumn - (forcedBreakOffsetVisibleColumn - breakOffsetVisibleColumn); if (remainingWidthOfNextLine <= tabSize) { const charCodeAtForcedBreakOffset = lineText.charCodeAt(forcedBreakOffset); let charWidth: number; @@ -289,7 +289,7 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC breakingOffsets[breakingOffsetsCount] = breakOffset; breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; breakingOffsetsCount++; - breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; + breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakColumn; while (prevIndex < 0 || (prevIndex < prevLen && prevBreakingOffsetsVisibleColumn[prevIndex] < breakOffsetVisibleColumn)) { prevIndex++; @@ -314,15 +314,15 @@ function createLineMappingFromPreviousLineMapping(classifier: WrappingCharacterC breakingOffsets.length = breakingOffsetsCount; breakingOffsetsVisibleColumn.length = breakingOffsetsCount; arrPool1 = previousBreakingData.breakOffsets; - arrPool2 = previousBreakingData.breakingOffsetsVisibleColumn; + arrPool2 = previousBreakingData.breakOffsetsVisibleColumn; previousBreakingData.breakOffsets = breakingOffsets; - previousBreakingData.breakingOffsetsVisibleColumn = breakingOffsetsVisibleColumn; + previousBreakingData.breakOffsetsVisibleColumn = breakingOffsetsVisibleColumn; previousBreakingData.wrappedTextIndentLength = wrappedTextIndentLength; return previousBreakingData; } -function createLineMapping(classifier: WrappingCharacterClassifier, lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): LineBreakingData | null { - if (firstLineBreakingColumn === -1) { +function createLineBreaks(classifier: WrappingCharacterClassifier, lineText: string, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): LineBreakData | null { + if (firstLineBreakColumn === -1) { return null; } @@ -331,8 +331,8 @@ function createLineMapping(classifier: WrappingCharacterClassifier, lineText: st return null; } - const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakingColumn, columnsForFullWidthChar, hardWrappingIndent); - const wrappedLineBreakingColumn = firstLineBreakingColumn - wrappedTextIndentLength; + const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakColumn, columnsForFullWidthChar, wrappingIndent); + const wrappedLineBreakColumn = firstLineBreakColumn - wrappedTextIndentLength; let breakingOffsets: number[] = []; let breakingOffsetsVisibleColumn: number[] = []; @@ -340,7 +340,7 @@ function createLineMapping(classifier: WrappingCharacterClassifier, lineText: st let breakOffset = 0; let breakOffsetVisibleColumn = 0; - let breakingColumn = firstLineBreakingColumn; + let breakingColumn = firstLineBreakColumn; let prevCharCode = lineText.charCodeAt(0); let prevCharCodeClass = classifier.get(prevCharCode); let visibleColumn = computeCharWidth(prevCharCode, 0, tabSize, columnsForFullWidthChar); @@ -381,7 +381,7 @@ function createLineMapping(classifier: WrappingCharacterClassifier, lineText: st if (visibleColumn > breakingColumn) { // We need to break at least before character at `i`: - if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakingColumn) { + if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakColumn) { // Cannot break at `breakOffset`, must break at `i` breakOffset = charStartOffset; breakOffsetVisibleColumn = visibleColumn - charWidth; @@ -390,7 +390,7 @@ function createLineMapping(classifier: WrappingCharacterClassifier, lineText: st breakingOffsets[breakingOffsetsCount] = breakOffset; breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; breakingOffsetsCount++; - breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakingColumn; + breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakColumn; breakOffset = 0; } @@ -406,7 +406,7 @@ function createLineMapping(classifier: WrappingCharacterClassifier, lineText: st breakingOffsets[breakingOffsetsCount] = len; breakingOffsetsVisibleColumn[breakingOffsetsCount] = visibleColumn; - return new LineBreakingData(breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); + return new LineBreakData(breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); } function computeCharWidth(charCode: number, visibleColumn: number, tabSize: number, columnsForFullWidthChar: number): number { @@ -436,9 +436,9 @@ function canBreak(prevCharCodeClass: CharacterClass, charCodeClass: CharacterCla ); } -function computeWrappedTextIndentLength(lineText: string, tabSize: number, firstLineBreakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): number { +function computeWrappedTextIndentLength(lineText: string, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): number { let wrappedTextIndentLength = 0; - if (hardWrappingIndent !== WrappingIndent.None) { + if (wrappingIndent !== WrappingIndent.None) { const firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineText); if (firstNonWhitespaceIndex !== -1) { // Track existing indent @@ -449,14 +449,14 @@ function computeWrappedTextIndentLength(lineText: string, tabSize: number, first } // Increase indent of continuation lines, if desired - const numberOfAdditionalTabs = (hardWrappingIndent === WrappingIndent.DeepIndent ? 2 : hardWrappingIndent === WrappingIndent.Indent ? 1 : 0); + const numberOfAdditionalTabs = (wrappingIndent === WrappingIndent.DeepIndent ? 2 : wrappingIndent === WrappingIndent.Indent ? 1 : 0); for (let i = 0; i < numberOfAdditionalTabs; i++) { const charWidth = tabCharacterWidth(wrappedTextIndentLength, tabSize); wrappedTextIndentLength += charWidth; } // Force sticking to beginning of line if no character would fit except for the indentation - if (wrappedTextIndentLength + columnsForFullWidthChar > firstLineBreakingColumn) { + if (wrappedTextIndentLength + columnsForFullWidthChar > firstLineBreakColumn) { wrappedTextIndentLength = 0; } } diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 9f66266bc94..726444f3b11 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -26,10 +26,10 @@ export class OutputPosition { } } -export class LineBreakingData { +export class LineBreakData { constructor( public breakOffsets: number[], - public breakingOffsetsVisibleColumn: number[], + public breakOffsetsVisibleColumn: number[], public wrappedTextIndentLength: number ) { } @@ -66,16 +66,16 @@ export class LineBreakingData { } } -export interface ILineMappingComputer { +export interface ILineBreaksComputer { /** - * Pass in previousLineBreakingData if the only difference is in breaking columns!!! + * Pass in `previousLineBreakData` if the only difference is in breaking columns!!! */ - addRequest(lineText: string, previousLineBreakingData: LineBreakingData | null): void; - finalize(): (LineBreakingData | null)[]; + addRequest(lineText: string, previousLineBreakData: LineBreakData | null): void; + finalize(): (LineBreakData | null)[]; } -export interface ILineMapperFactory { - createLineMappingComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineMappingComputer; +export interface ILineBreaksComputerFactory { + createLineBreaksComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineBreaksComputer; } export interface ISimpleModel { @@ -91,7 +91,7 @@ export interface ISplitLine { isVisible(): boolean; setVisible(isVisible: boolean): ISplitLine; - getLineBreakingData(): LineBreakingData | null; + getLineBreakData(): LineBreakData | null; getViewLineCount(): number; getViewLineContent(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): string; getViewLineLength(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): number; @@ -113,11 +113,11 @@ export interface IViewModelLinesCollection extends IDisposable { getHiddenAreas(): Range[]; setHiddenAreas(_ranges: Range[]): boolean; - createLineMappingComputer(): ILineMappingComputer; + createLineBreaksComputer(): ILineBreaksComputer; onModelFlushed(): void; onModelLinesDeleted(versionId: number, fromLineNumber: number, toLineNumber: number): viewEvents.ViewLinesDeletedEvent | null; - onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, linesMappings: (LineBreakingData | null)[]): viewEvents.ViewLinesInsertedEvent | null; - onModelLineChanged(versionId: number, lineNumber: number, lineMapping: LineBreakingData | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null]; + onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, lineBreaks: (LineBreakData | null)[]): viewEvents.ViewLinesInsertedEvent | null; + onModelLineChanged(versionId: number, lineNumber: number, lineBreakData: LineBreakData | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null]; acceptVersionId(versionId: number): void; getViewLineCount(): number; @@ -277,11 +277,11 @@ export class SplitLinesCollection implements IViewModelLinesCollection { private prefixSumComputer!: LineNumberMapper; - private readonly linePositionMapperFactory: ILineMapperFactory; + private readonly linePositionMapperFactory: ILineBreaksComputerFactory; private hiddenAreasIds!: string[]; - constructor(model: ITextModel, linePositionMapperFactory: ILineMapperFactory, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent) { + constructor(model: ITextModel, linePositionMapperFactory: ILineBreaksComputerFactory, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent) { this.model = model; this._validModelVersionId = -1; this.tabSize = tabSize; @@ -301,7 +301,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return new CoordinatesConverter(this); } - private _constructLines(resetHiddenAreas: boolean, previousLineMapping: ((LineBreakingData | null)[]) | null): void { + private _constructLines(resetHiddenAreas: boolean, previousLineBreaks: ((LineBreakData | null)[]) | null): void { this.lines = []; if (resetHiddenAreas) { @@ -310,11 +310,11 @@ export class SplitLinesCollection implements IViewModelLinesCollection { let linesContent = this.model.getLinesContent(); const lineCount = linesContent.length; - const lineMappingComputer = this.linePositionMapperFactory.createLineMappingComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent); + const lineBreaksComputer = this.linePositionMapperFactory.createLineBreaksComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent); for (let i = 0; i < lineCount; i++) { - lineMappingComputer.addRequest(linesContent[i], previousLineMapping ? previousLineMapping[i] : null); + lineBreaksComputer.addRequest(linesContent[i], previousLineBreaks ? previousLineBreaks[i] : null); } - const lineMappings = lineMappingComputer.finalize(); + const linesBreaks = lineBreaksComputer.finalize(); let values: number[] = []; @@ -334,7 +334,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } let isInHiddenArea = (lineNumber >= hiddenAreaStart && lineNumber <= hiddenAreaEnd); - let line = createSplitLine(lineMappings[i], !isInHiddenArea); + let line = createSplitLine(linesBreaks[i], !isInHiddenArea); values[i] = line.getViewLineCount(); this.lines[i] = line; } @@ -481,21 +481,21 @@ export class SplitLinesCollection implements IViewModelLinesCollection { this.wrappingColumn = wrappingColumn; this.columnsForFullWidthChar = columnsForFullWidthChar; - let previousLineMapping: ((LineBreakingData | null)[]) | null = null; + let previousLineBreaks: ((LineBreakData | null)[]) | null = null; if (onlyWrappingColumnChanged) { - previousLineMapping = []; + previousLineBreaks = []; for (let i = 0, len = this.lines.length; i < len; i++) { - previousLineMapping[i] = this.lines[i].getLineBreakingData(); + previousLineBreaks[i] = this.lines[i].getLineBreakData(); } } - this._constructLines(/*resetHiddenAreas*/false, previousLineMapping); + this._constructLines(/*resetHiddenAreas*/false, previousLineBreaks); return true; } - public createLineMappingComputer(): ILineMappingComputer { - return this.linePositionMapperFactory.createLineMappingComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent); + public createLineBreaksComputer(): ILineBreaksComputer { + return this.linePositionMapperFactory.createLineBreaksComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent); } public onModelFlushed(): void { @@ -518,7 +518,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return new viewEvents.ViewLinesDeletedEvent(outputFromLineNumber, outputToLineNumber); } - public onModelLinesInserted(versionId: number, fromLineNumber: number, _toLineNumber: number, linesMappings: (LineBreakingData | null)[]): viewEvents.ViewLinesInsertedEvent | null { + public onModelLinesInserted(versionId: number, fromLineNumber: number, _toLineNumber: number, lineBreaks: (LineBreakData | null)[]): viewEvents.ViewLinesInsertedEvent | null { if (versionId <= this._validModelVersionId) { // Here we check for versionId in case the lines were reconstructed in the meantime. // We don't want to apply stale change events on top of a newer read model state. @@ -541,8 +541,8 @@ export class SplitLinesCollection implements IViewModelLinesCollection { let insertLines: ISplitLine[] = []; let insertPrefixSumValues: number[] = []; - for (let i = 0, len = linesMappings.length; i < len; i++) { - let line = createSplitLine(linesMappings[i], !isInHiddenArea); + for (let i = 0, len = lineBreaks.length; i < len; i++) { + let line = createSplitLine(lineBreaks[i], !isInHiddenArea); insertLines.push(line); let outputLineCount = line.getViewLineCount(); @@ -558,7 +558,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return new viewEvents.ViewLinesInsertedEvent(outputFromLineNumber, outputFromLineNumber + totalOutputLineCount - 1); } - public onModelLineChanged(versionId: number, lineNumber: number, lineMapping: LineBreakingData | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { + public onModelLineChanged(versionId: number, lineNumber: number, lineBreakData: LineBreakData | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { if (versionId <= this._validModelVersionId) { // Here we check for versionId in case the lines were reconstructed in the meantime. // We don't want to apply stale change events on top of a newer read model state. @@ -569,7 +569,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { let oldOutputLineCount = this.lines[lineIndex].getViewLineCount(); let isVisible = this.lines[lineIndex].isVisible(); - let line = createSplitLine(lineMapping, isVisible); + let line = createSplitLine(lineBreakData, isVisible); this.lines[lineIndex] = line; let newOutputLineCount = this.lines[lineIndex].getViewLineCount(); @@ -1019,7 +1019,7 @@ class VisibleIdentitySplitLine implements ISplitLine { return InvisibleIdentitySplitLine.INSTANCE; } - public getLineBreakingData(): LineBreakingData | null { + public getLineBreakData(): LineBreakData | null { return null; } @@ -1094,7 +1094,7 @@ class InvisibleIdentitySplitLine implements ISplitLine { return VisibleIdentitySplitLine.INSTANCE; } - public getLineBreakingData(): LineBreakingData | null { + public getLineBreakData(): LineBreakData | null { return null; } @@ -1141,11 +1141,11 @@ class InvisibleIdentitySplitLine implements ISplitLine { export class SplitLine implements ISplitLine { - private readonly _lineBreakingData: LineBreakingData; + private readonly _lineBreakData: LineBreakData; private _isVisible: boolean; - constructor(lineBreakingData: LineBreakingData, isVisible: boolean) { - this._lineBreakingData = lineBreakingData; + constructor(lineBreakData: LineBreakData, isVisible: boolean) { + this._lineBreakData = lineBreakData; this._isVisible = isVisible; } @@ -1158,26 +1158,26 @@ export class SplitLine implements ISplitLine { return this; } - public getLineBreakingData(): LineBreakingData | null { - return this._lineBreakingData; + public getLineBreakData(): LineBreakData | null { + return this._lineBreakData; } public getViewLineCount(): number { if (!this._isVisible) { return 0; } - return this._lineBreakingData.breakOffsets.length; + return this._lineBreakData.breakOffsets.length; } private getInputStartOffsetOfOutputLineIndex(outputLineIndex: number): number { - return LineBreakingData.getInputOffsetOfOutputPosition(this._lineBreakingData.breakOffsets, outputLineIndex, 0); + return LineBreakData.getInputOffsetOfOutputPosition(this._lineBreakData.breakOffsets, outputLineIndex, 0); } private getInputEndOffsetOfOutputLineIndex(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): number { - if (outputLineIndex + 1 === this._lineBreakingData.breakOffsets.length) { + if (outputLineIndex + 1 === this._lineBreakData.breakOffsets.length) { return model.getLineMaxColumn(modelLineNumber) - 1; } - return LineBreakingData.getInputOffsetOfOutputPosition(this._lineBreakingData.breakOffsets, outputLineIndex + 1, 0); + return LineBreakData.getInputOffsetOfOutputPosition(this._lineBreakData.breakOffsets, outputLineIndex + 1, 0); } public getViewLineContent(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): string { @@ -1194,7 +1194,7 @@ export class SplitLine implements ISplitLine { }); if (outputLineIndex > 0) { - r = spaces(this._lineBreakingData.wrappedTextIndentLength) + r; + r = spaces(this._lineBreakData.wrappedTextIndentLength) + r; } return r; @@ -1209,7 +1209,7 @@ export class SplitLine implements ISplitLine { let r = endOffset - startOffset; if (outputLineIndex > 0) { - r = this._lineBreakingData.wrappedTextIndentLength + r; + r = this._lineBreakData.wrappedTextIndentLength + r; } return r; @@ -1220,7 +1220,7 @@ export class SplitLine implements ISplitLine { throw new Error('Not supported'); } if (outputLineIndex > 0) { - return this._lineBreakingData.wrappedTextIndentLength + 1; + return this._lineBreakData.wrappedTextIndentLength + 1; } return 1; } @@ -1248,21 +1248,21 @@ export class SplitLine implements ISplitLine { }); if (outputLineIndex > 0) { - lineContent = spaces(this._lineBreakingData.wrappedTextIndentLength) + lineContent; + lineContent = spaces(this._lineBreakData.wrappedTextIndentLength) + lineContent; } - let minColumn = (outputLineIndex > 0 ? this._lineBreakingData.wrappedTextIndentLength + 1 : 1); + let minColumn = (outputLineIndex > 0 ? this._lineBreakData.wrappedTextIndentLength + 1 : 1); let maxColumn = lineContent.length + 1; let continuesWithWrappedLine = (outputLineIndex + 1 < this.getViewLineCount()); let deltaStartIndex = 0; if (outputLineIndex > 0) { - deltaStartIndex = this._lineBreakingData.wrappedTextIndentLength; + deltaStartIndex = this._lineBreakData.wrappedTextIndentLength; } let lineTokens = model.getLineTokens(modelLineNumber); - const startVisibleColumn = (outputLineIndex === 0 ? 0 : this._lineBreakingData.breakingOffsetsVisibleColumn[outputLineIndex - 1]); + const startVisibleColumn = (outputLineIndex === 0 ? 0 : this._lineBreakData.breakOffsetsVisibleColumn[outputLineIndex - 1]); return new ViewLineData( lineContent, @@ -1295,25 +1295,25 @@ export class SplitLine implements ISplitLine { } let adjustedColumn = outputColumn - 1; if (outputLineIndex > 0) { - if (adjustedColumn < this._lineBreakingData.wrappedTextIndentLength) { + if (adjustedColumn < this._lineBreakData.wrappedTextIndentLength) { adjustedColumn = 0; } else { - adjustedColumn -= this._lineBreakingData.wrappedTextIndentLength; + adjustedColumn -= this._lineBreakData.wrappedTextIndentLength; } } - return LineBreakingData.getInputOffsetOfOutputPosition(this._lineBreakingData.breakOffsets, outputLineIndex, adjustedColumn) + 1; + return LineBreakData.getInputOffsetOfOutputPosition(this._lineBreakData.breakOffsets, outputLineIndex, adjustedColumn) + 1; } public getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position { if (!this._isVisible) { throw new Error('Not supported'); } - let r = LineBreakingData.getOutputPositionOfInputOffset(this._lineBreakingData.breakOffsets, inputColumn - 1); + let r = LineBreakData.getOutputPositionOfInputOffset(this._lineBreakData.breakOffsets, inputColumn - 1); let outputLineIndex = r.outputLineIndex; let outputColumn = r.outputOffset + 1; if (outputLineIndex > 0) { - outputColumn += this._lineBreakingData.wrappedTextIndentLength; + outputColumn += this._lineBreakData.wrappedTextIndentLength; } // console.log('in -> out ' + deltaLineNumber + ',' + inputColumn + ' ===> ' + (deltaLineNumber+outputLineIndex) + ',' + outputColumn); @@ -1324,7 +1324,7 @@ export class SplitLine implements ISplitLine { if (!this._isVisible) { throw new Error('Not supported'); } - const r = LineBreakingData.getOutputPositionOfInputOffset(this._lineBreakingData.breakOffsets, inputColumn - 1); + const r = LineBreakData.getOutputPositionOfInputOffset(this._lineBreakData.breakOffsets, inputColumn - 1); return (deltaLineNumber + r.outputLineIndex); } } @@ -1342,15 +1342,15 @@ function _makeSpaces(count: number): string { return new Array(count + 1).join(' '); } -function createSplitLine(lineMapping: LineBreakingData | null, isVisible: boolean): ISplitLine { - if (lineMapping === null) { +function createSplitLine(lineBreakData: LineBreakData | null, isVisible: boolean): ISplitLine { + if (lineBreakData === null) { // No mapping needed if (isVisible) { return VisibleIdentitySplitLine.INSTANCE; } return InvisibleIdentitySplitLine.INSTANCE; } else { - return new SplitLine(lineMapping, isVisible); + return new SplitLine(lineBreakData, isVisible); } } @@ -1440,10 +1440,10 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return false; } - public createLineMappingComputer(): ILineMappingComputer { + public createLineBreaksComputer(): ILineBreaksComputer { let result: null[] = []; return { - addRequest: (lineText: string, previousLineBreakingData: LineBreakingData | null) => { + addRequest: (lineText: string, previousLineBreakData: LineBreakData | null) => { result.push(null); }, finalize: () => { @@ -1459,11 +1459,11 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return new viewEvents.ViewLinesDeletedEvent(fromLineNumber, toLineNumber); } - public onModelLinesInserted(_versionId: number, fromLineNumber: number, toLineNumber: number, linesMappings: (LineBreakingData | null)[]): viewEvents.ViewLinesInsertedEvent | null { + public onModelLinesInserted(_versionId: number, fromLineNumber: number, toLineNumber: number, lineBreaks: (LineBreakData | null)[]): viewEvents.ViewLinesInsertedEvent | null { return new viewEvents.ViewLinesInsertedEvent(fromLineNumber, toLineNumber); } - public onModelLineChanged(_versionId: number, lineNumber: number, lineMapping: LineBreakingData | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { + public onModelLineChanged(_versionId: number, lineNumber: number, lineBreakData: LineBreakData | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { return [false, new viewEvents.ViewLinesChangedEvent(lineNumber, lineNumber), null, null]; } diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 55100bc6c23..718c9fc090e 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -18,7 +18,7 @@ import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTokensColorTracker'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout'; -import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection, ILineMapperFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection, ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; import { ITheme } from 'vs/platform/theme/common/themeService'; @@ -46,7 +46,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel editorId: number, configuration: editorCommon.IConfiguration, model: ITextModel, - lineMapperFactory: ILineMapperFactory, + lineMapperFactory: ILineBreaksComputerFactory, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable ) { super(); @@ -197,23 +197,23 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const versionId = e.versionId; // Do a first pass to compute line mappings, and a second pass to actually interpret them - const lineMappingComputer = this.lines.createLineMappingComputer(); + const lineBreaksComputer = this.lines.createLineBreaksComputer(); for (const change of changes) { switch (change.changeType) { case textModelEvents.RawContentChangedType.LinesInserted: { for (const line of change.detail) { - lineMappingComputer.addRequest(line, null); + lineBreaksComputer.addRequest(line, null); } break; } case textModelEvents.RawContentChangedType.LineChanged: { - lineMappingComputer.addRequest(change.detail, null); + lineBreaksComputer.addRequest(change.detail, null); break; } } } - const lineMappings = lineMappingComputer.finalize(); - let lineMappingsOffset = 0; + const lineBreaks = lineBreaksComputer.finalize(); + let lineBreaksOffset = 0; for (const change of changes) { @@ -236,10 +236,10 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel break; } case textModelEvents.RawContentChangedType.LinesInserted: { - const insertedLinesMappings = lineMappings.slice(lineMappingsOffset, lineMappingsOffset + change.detail.length); - lineMappingsOffset += change.detail.length; + const insertedLineBreaks = lineBreaks.slice(lineBreaksOffset, lineBreaksOffset + change.detail.length); + lineBreaksOffset += change.detail.length; - const linesInsertedEvent = this.lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLinesMappings); + const linesInsertedEvent = this.lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLineBreaks); if (linesInsertedEvent !== null) { eventsCollector.emit(linesInsertedEvent); this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber); @@ -248,10 +248,10 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel break; } case textModelEvents.RawContentChangedType.LineChanged: { - const changedLineMapping = lineMappings[lineMappingsOffset]; - lineMappingsOffset++; + const changedLineBreakData = lineBreaks[lineBreaksOffset]; + lineBreaksOffset++; - const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this.lines.onModelLineChanged(versionId, change.lineNumber, changedLineMapping); + const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this.lines.onModelLineChanged(versionId, change.lineNumber, changedLineBreakData); hadModelLineChangeThatChangedLineMapping = lineMappingChanged; if (linesChangedEvent) { eventsCollector.emit(linesChangedEvent); diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index 5c3716ed79c..2eecd5ae2c5 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -381,7 +381,6 @@ export class LineCommentCommand implements editorCommon.ICommand { return res; } - // TODO@Alex -> duplicated in characterHardWrappingLineMapper private static nextVisibleColumn(currentVisibleColumn: number, tabSize: number, isTab: boolean, columnSize: number): number { if (isTab) { return currentVisibleColumn + (tabSize - (currentVisibleColumn % tabSize)); diff --git a/src/vs/editor/test/browser/commands/sideEditing.test.ts b/src/vs/editor/test/browser/commands/sideEditing.test.ts index 205a169c495..67ae4e4d174 100644 --- a/src/vs/editor/test/browser/commands/sideEditing.test.ts +++ b/src/vs/editor/test/browser/commands/sideEditing.test.ts @@ -14,7 +14,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; -import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; +import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; function testCommand(lines: string[], selections: Selection[], edits: IIdentifiedSingleEditOperation[], expectedLines: string[], expectedSelections: Selection[]): void { withTestCodeEditor(lines, {}, (editor, cursor) => { @@ -201,7 +201,7 @@ suite('SideEditing', () => { function _runTest(selection: Selection, editRange: Range, editText: string, editForceMoveMarkers: boolean, expected: Selection, msg: string): void { const model = TextModel.createFromString(LINES.join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); + const viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); const cursor = new Cursor(config, model, viewModel); cursor.setSelections('tests', [selection]); diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 9031f4056de..7d82c86f55e 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -25,7 +25,7 @@ import { IRelaxedTextModelCreationOptions, createTextModel } from 'vs/editor/tes import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { javascriptOnEnterRules } from 'vs/editor/test/common/modes/supports/javascriptOnEnterRules'; -import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; +import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; const H = Handler; @@ -153,7 +153,7 @@ suite('Editor Controller - Cursor', () => { thisModel = createTextModel(text); thisConfiguration = new TestConfiguration({}); - thisViewModel = new ViewModel(0, thisConfiguration, thisModel, CharacterHardWrappingLineMapperFactory.create(thisConfiguration.options), null!); + thisViewModel = new ViewModel(0, thisConfiguration, thisModel, MonospaceLineBreaksComputerFactory.create(thisConfiguration.options), null!); thisCursor = new Cursor(thisConfiguration, thisModel, thisViewModel); }); @@ -777,7 +777,7 @@ suite('Editor Controller - Cursor', () => { 'var newer = require("gulp-newer");', ].join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); + const viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 1, 4, false); @@ -817,7 +817,7 @@ suite('Editor Controller - Cursor', () => { '', ].join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); + const viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 10, 10, false); @@ -881,7 +881,7 @@ suite('Editor Controller - Cursor', () => { '', ].join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); + const viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 10, 10, false); @@ -930,7 +930,7 @@ suite('Editor Controller - Cursor', () => { 'var newer = require("gulp-newer");', ].join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); + const viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 1, 4, false); @@ -2075,7 +2075,7 @@ suite('Editor Controller - Regression tests', () => { wordWrap: 'wordWrapColumn', wordWrapColumn: 100 }); - const viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); + const viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 1, 43, false); @@ -3835,7 +3835,7 @@ function usingCursor(opts: ICursorOpts, callback: (model: TextModel, cursor: Cur let model = createTextModel(opts.text.join('\n'), opts.modelOpts, opts.languageIdentifier); model.forceTokenization(model.getLineCount()); let config = new TestConfiguration(opts.editorOpts || {}); - let viewModel = new ViewModel(0, config, model, CharacterHardWrappingLineMapperFactory.create(config.options), null!); + let viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); let cursor = new Cursor(config, model, viewModel); callback(model, cursor); diff --git a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts index dad6b955cda..117cf54372d 100644 --- a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts +++ b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts @@ -13,7 +13,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { TextModel } from 'vs/editor/common/model/textModel'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; -import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; +import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; suite('Cursor move command test', () => { @@ -33,7 +33,7 @@ suite('Cursor move command test', () => { thisModel = TextModel.createFromString(text); thisConfiguration = new TestConfiguration({}); - thisViewModel = new ViewModel(0, thisConfiguration, thisModel, CharacterHardWrappingLineMapperFactory.create(thisConfiguration.options), null!); + thisViewModel = new ViewModel(0, thisConfiguration, thisModel, MonospaceLineBreaksComputerFactory.create(thisConfiguration.options), null!); thisCursor = new Cursor(thisConfiguration, thisModel, thisViewModel); }); diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts similarity index 50% rename from src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts rename to src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts index db130c390b1..625c678ff47 100644 --- a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts +++ b/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { WrappingIndent, EditorOptions } from 'vs/editor/common/config/editorOptions'; -import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; -import { ILineMapperFactory, LineBreakingData } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; +import { ILineBreaksComputerFactory, LineBreakData } from 'vs/editor/common/viewModel/splitLinesCollection'; function parseAnnotatedText(annotatedText: string): { text: string; indices: number[]; } { let text = ''; @@ -22,13 +22,13 @@ function parseAnnotatedText(annotatedText: string): { text: string; indices: num return { text: text, indices: indices }; } -function toAnnotatedText(text: string, lineBreakingData: LineBreakingData | null): string { +function toAnnotatedText(text: string, lineBreakData: LineBreakData | null): string { // Insert line break markers again, according to algorithm let actualAnnotatedText = ''; - if (lineBreakingData) { + if (lineBreakData) { let previousLineIndex = 0; for (let i = 0, len = text.length; i < len; i++) { - let r = LineBreakingData.getOutputPositionOfInputOffset(lineBreakingData.breakOffsets, i); + let r = LineBreakData.getOutputPositionOfInputOffset(lineBreakData.breakOffsets, i); if (previousLineIndex !== r.outputLineIndex) { previousLineIndex = r.outputLineIndex; actualAnnotatedText += '|'; @@ -42,142 +42,142 @@ function toAnnotatedText(text: string, lineBreakingData: LineBreakingData | null return actualAnnotatedText; } -function getLineBreakingData(factory: ILineMapperFactory, tabSize: number, breakAfter: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, text: string, previousLineBreakingData: LineBreakingData | null): LineBreakingData | null { - const lineMappingComputer = factory.createLineMappingComputer(tabSize, breakAfter, columnsForFullWidthChar, wrappingIndent); - const previousLineBreakingDataClone = previousLineBreakingData ? new LineBreakingData(previousLineBreakingData.breakOffsets.slice(0), previousLineBreakingData.breakingOffsetsVisibleColumn.slice(0), previousLineBreakingData.wrappedTextIndentLength) : null; - lineMappingComputer.addRequest(text, previousLineBreakingDataClone); - return lineMappingComputer.finalize()[0]; +function getLineBreakData(factory: ILineBreaksComputerFactory, tabSize: number, breakAfter: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, text: string, previousLineBreakData: LineBreakData | null): LineBreakData | null { + const lineBreaksComputer = factory.createLineBreaksComputer(tabSize, breakAfter, columnsForFullWidthChar, wrappingIndent); + const previousLineBreakDataClone = previousLineBreakData ? new LineBreakData(previousLineBreakData.breakOffsets.slice(0), previousLineBreakData.breakOffsetsVisibleColumn.slice(0), previousLineBreakData.wrappedTextIndentLength) : null; + lineBreaksComputer.addRequest(text, previousLineBreakDataClone); + return lineBreaksComputer.finalize()[0]; } -function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None): LineBreakingData | null { +function assertLineBreaks(factory: ILineBreaksComputerFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None): LineBreakData | null { // Create version of `annotatedText` with line break markers removed const text = parseAnnotatedText(annotatedText).text; - const lineBreakingData = getLineBreakingData(factory, tabSize, breakAfter, 2, wrappingIndent, text, null); - const actualAnnotatedText = toAnnotatedText(text, lineBreakingData); + const lineBreakData = getLineBreakData(factory, tabSize, breakAfter, 2, wrappingIndent, text, null); + const actualAnnotatedText = toAnnotatedText(text, lineBreakData); assert.equal(actualAnnotatedText, annotatedText); - return lineBreakingData; + return lineBreakData; } -suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { - test('CharacterHardWrappingLineMapper', () => { +suite('Editor ViewModel - MonospaceLineBreaksComputer', () => { + test('MonospaceLineBreaksComputer', () => { - let factory = new CharacterHardWrappingLineMapperFactory('(', '\t).'); + let factory = new MonospaceLineBreaksComputerFactory('(', '\t).'); // Empty string - assertLineMapping(factory, 4, 5, ''); + assertLineBreaks(factory, 4, 5, ''); // No wrapping if not necessary - assertLineMapping(factory, 4, 5, 'aaa'); - assertLineMapping(factory, 4, 5, 'aaaaa'); - assertLineMapping(factory, 4, -1, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); + assertLineBreaks(factory, 4, 5, 'aaa'); + assertLineBreaks(factory, 4, 5, 'aaaaa'); + assertLineBreaks(factory, 4, -1, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); // Acts like hard wrapping if no char found - assertLineMapping(factory, 4, 5, 'aaaaa|a'); + assertLineBreaks(factory, 4, 5, 'aaaaa|a'); // Honors wrapping character - assertLineMapping(factory, 4, 5, 'aaaaa|.'); - assertLineMapping(factory, 4, 5, 'aaaaa|a.|aaa.|aa'); - assertLineMapping(factory, 4, 5, 'aaaaa|a..|aaa.|aa'); - assertLineMapping(factory, 4, 5, 'aaaaa|a...|aaa.|aa'); - assertLineMapping(factory, 4, 5, 'aaaaa|a....|aaa.|aa'); + assertLineBreaks(factory, 4, 5, 'aaaaa|.'); + assertLineBreaks(factory, 4, 5, 'aaaaa|a.|aaa.|aa'); + assertLineBreaks(factory, 4, 5, 'aaaaa|a..|aaa.|aa'); + assertLineBreaks(factory, 4, 5, 'aaaaa|a...|aaa.|aa'); + assertLineBreaks(factory, 4, 5, 'aaaaa|a....|aaa.|aa'); // Honors tabs when computing wrapping position - assertLineMapping(factory, 4, 5, '\t'); - assertLineMapping(factory, 4, 5, '\t|aaa'); - assertLineMapping(factory, 4, 5, '\t|a\t|aa'); - assertLineMapping(factory, 4, 5, 'aa\ta'); - assertLineMapping(factory, 4, 5, 'aa\t|aa'); + assertLineBreaks(factory, 4, 5, '\t'); + assertLineBreaks(factory, 4, 5, '\t|aaa'); + assertLineBreaks(factory, 4, 5, '\t|a\t|aa'); + assertLineBreaks(factory, 4, 5, 'aa\ta'); + assertLineBreaks(factory, 4, 5, 'aa\t|aa'); // Honors wrapping before characters (& gives it priority) - assertLineMapping(factory, 4, 5, 'aaa.|aa'); - assertLineMapping(factory, 4, 5, 'aaa(.|aa'); + assertLineBreaks(factory, 4, 5, 'aaa.|aa'); + assertLineBreaks(factory, 4, 5, 'aaa(.|aa'); // Honors wrapping after characters (& gives it priority) - assertLineMapping(factory, 4, 5, 'aaa))|).aaa'); - assertLineMapping(factory, 4, 5, 'aaa))|).|aaaa'); - assertLineMapping(factory, 4, 5, 'aaa)|().|aaa'); - assertLineMapping(factory, 4, 5, 'aaa(|().|aaa'); - assertLineMapping(factory, 4, 5, 'aa.(|().|aaa'); - assertLineMapping(factory, 4, 5, 'aa.(.|).aaa'); + assertLineBreaks(factory, 4, 5, 'aaa))|).aaa'); + assertLineBreaks(factory, 4, 5, 'aaa))|).|aaaa'); + assertLineBreaks(factory, 4, 5, 'aaa)|().|aaa'); + assertLineBreaks(factory, 4, 5, 'aaa(|().|aaa'); + assertLineBreaks(factory, 4, 5, 'aa.(|().|aaa'); + assertLineBreaks(factory, 4, 5, 'aa.(.|).aaa'); }); - function assertIncrementalLineMapping(factory: ILineMapperFactory, text: string, tabSize: number, breakAfter1: number, annotatedText1: string, breakAfter2: number, annotatedText2: string, wrappingIndent = WrappingIndent.None): void { + function assertIncrementalLineBreaks(factory: ILineBreaksComputerFactory, text: string, tabSize: number, breakAfter1: number, annotatedText1: string, breakAfter2: number, annotatedText2: string, wrappingIndent = WrappingIndent.None): void { // sanity check the test assert.equal(text, parseAnnotatedText(annotatedText1).text); assert.equal(text, parseAnnotatedText(annotatedText2).text); // check that the direct mapping is ok for 1 - const directLineBreakingData1 = getLineBreakingData(factory, tabSize, breakAfter1, 2, wrappingIndent, text, null); - assert.equal(toAnnotatedText(text, directLineBreakingData1), annotatedText1); + const directLineBreakData1 = getLineBreakData(factory, tabSize, breakAfter1, 2, wrappingIndent, text, null); + assert.equal(toAnnotatedText(text, directLineBreakData1), annotatedText1); // check that the direct mapping is ok for 2 - const directLineBreakingData2 = getLineBreakingData(factory, tabSize, breakAfter2, 2, wrappingIndent, text, null); - assert.equal(toAnnotatedText(text, directLineBreakingData2), annotatedText2); + const directLineBreakData2 = getLineBreakData(factory, tabSize, breakAfter2, 2, wrappingIndent, text, null); + assert.equal(toAnnotatedText(text, directLineBreakData2), annotatedText2); // check that going from 1 to 2 is ok - const lineBreakingData2from1 = getLineBreakingData(factory, tabSize, breakAfter2, 2, wrappingIndent, text, directLineBreakingData1); - assert.equal(toAnnotatedText(text, lineBreakingData2from1), annotatedText2); - assert.deepEqual(lineBreakingData2from1, directLineBreakingData2); + const lineBreakData2from1 = getLineBreakData(factory, tabSize, breakAfter2, 2, wrappingIndent, text, directLineBreakData1); + assert.equal(toAnnotatedText(text, lineBreakData2from1), annotatedText2); + assert.deepEqual(lineBreakData2from1, directLineBreakData2); // check that going from 2 to 1 is ok - const lineBreakingData1from2 = getLineBreakingData(factory, tabSize, breakAfter1, 2, wrappingIndent, text, directLineBreakingData2); - assert.equal(toAnnotatedText(text, lineBreakingData1from2), annotatedText1); - assert.deepEqual(lineBreakingData1from2, directLineBreakingData1); + const lineBreakData1from2 = getLineBreakData(factory, tabSize, breakAfter1, 2, wrappingIndent, text, directLineBreakData2); + assert.equal(toAnnotatedText(text, lineBreakData1from2), annotatedText1); + assert.deepEqual(lineBreakData1from2, directLineBreakData1); } - test('CharacterHardWrappingLineMapper incremental 1', () => { + test('MonospaceLineBreaksComputer incremental 1', () => { - let factory = new CharacterHardWrappingLineMapperFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); + let factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); - assertIncrementalLineMapping( + assertIncrementalLineBreaks( factory, 'just some text and more', 4, 10, 'just some |text and |more', 15, 'just some text |and more' ); - assertIncrementalLineMapping( + assertIncrementalLineBreaks( factory, 'Cu scripserit suscipiantur eos, in affert pericula contentiones sed, cetero sanctus et pro. Ius vidit magna regione te, sit ei elaboraret liberavisse. Mundi verear eu mea, eam vero scriptorem in, vix in menandri assueverit. Natum definiebas cu vim. Vim doming vocibus efficiantur id. In indoctum deseruisse voluptatum vim, ad debitis verterem sed.', 4, 47, 'Cu scripserit suscipiantur eos, in affert |pericula contentiones sed, cetero sanctus et |pro. Ius vidit magna regione te, sit ei |elaboraret liberavisse. Mundi verear eu mea, |eam vero scriptorem in, vix in menandri |assueverit. Natum definiebas cu vim. Vim |doming vocibus efficiantur id. In indoctum |deseruisse voluptatum vim, ad debitis verterem |sed.', 142, 'Cu scripserit suscipiantur eos, in affert pericula contentiones sed, cetero sanctus et pro. Ius vidit magna regione te, sit ei elaboraret |liberavisse. Mundi verear eu mea, eam vero scriptorem in, vix in menandri assueverit. Natum definiebas cu vim. Vim doming vocibus efficiantur |id. In indoctum deseruisse voluptatum vim, ad debitis verterem sed.', ); - assertIncrementalLineMapping( + assertIncrementalLineBreaks( factory, 'An his legere persecuti, oblique delicata efficiantur ex vix, vel at graecis officiis maluisset. Et per impedit voluptua, usu discere maiorum at. Ut assum ornatus temporibus vis, an sea melius pericula. Ea dicunt oblique phaedrum nam, eu duo movet nobis. His melius facilis eu, vim malorum temporibus ne. Nec no sale regione, meliore civibus placerat id eam. Mea alii fabulas definitionem te, agam volutpat ad vis, et per bonorum nonumes repudiandae.', 4, 57, 'An his legere persecuti, oblique delicata efficiantur ex |vix, vel at graecis officiis maluisset. Et per impedit |voluptua, usu discere maiorum at. Ut assum ornatus |temporibus vis, an sea melius pericula. Ea dicunt |oblique phaedrum nam, eu duo movet nobis. His melius |facilis eu, vim malorum temporibus ne. Nec no sale |regione, meliore civibus placerat id eam. Mea alii |fabulas definitionem te, agam volutpat ad vis, et per |bonorum nonumes repudiandae.', 58, 'An his legere persecuti, oblique delicata efficiantur ex |vix, vel at graecis officiis maluisset. Et per impedit |voluptua, usu discere maiorum at. Ut assum ornatus |temporibus vis, an sea melius pericula. Ea dicunt oblique |phaedrum nam, eu duo movet nobis. His melius facilis eu, |vim malorum temporibus ne. Nec no sale regione, meliore |civibus placerat id eam. Mea alii fabulas definitionem te,| agam volutpat ad vis, et per bonorum nonumes repudiandae.' ); - assertIncrementalLineMapping( + assertIncrementalLineBreaks( factory, '\t\t"owner": "vscode",', 4, 14, '\t\t"owner|": |"vscod|e",', 16, '\t\t"owner":| |"vscode"|,', WrappingIndent.Same ); - assertIncrementalLineMapping( + assertIncrementalLineBreaks( factory, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇&👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬', 4, 51, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇&|👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬', 50, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇|&|👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬', WrappingIndent.Same ); - assertIncrementalLineMapping( + assertIncrementalLineBreaks( factory, '🐇👬&🌞🌖', 4, 5, '🐇👬&|🌞🌖', 4, '🐇👬|&|🌞🌖', WrappingIndent.Same ); - assertIncrementalLineMapping( + assertIncrementalLineBreaks( factory, '\t\tfunc(\'🌞🏇🍼🌞🏇🍼🐇&👬🌖🌞👬🌖🌞🏇🍼🐇👬\', WrappingIndent.Same);', 4, 26, '\t\tfunc|(\'🌞🏇🍼🌞🏇🍼🐇&|👬🌖🌞👬🌖🌞🏇🍼🐇|👬\', |WrappingIndent.|Same);', 27, '\t\tfunc|(\'🌞🏇🍼🌞🏇🍼🐇&|👬🌖🌞👬🌖🌞🏇🍼🐇|👬\', |WrappingIndent.|Same);', WrappingIndent.Same ); - assertIncrementalLineMapping( + assertIncrementalLineBreaks( factory, 'factory, "xtxtfunc(x"🌞🏇🍼🌞🏇🍼🐇&👬🌖🌞👬🌖🌞🏇🍼🐇👬x"', 4, 16, 'factory, |"xtxtfunc|(x"🌞🏇🍼🌞🏇🍼|🐇&|👬🌖🌞👬🌖🌞🏇🍼|🐇👬x"', 17, 'factory, |"xtxtfunc|(x"🌞🏇🍼🌞🏇🍼🐇|&👬🌖🌞👬🌖🌞🏇🍼|🐇👬x"', @@ -186,50 +186,50 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { }); - test('CharacterHardWrappingLineMapper - CJK and Kinsoku Shori', () => { - let factory = new CharacterHardWrappingLineMapperFactory('(', '\t)'); - assertLineMapping(factory, 4, 5, 'aa \u5b89|\u5b89'); - assertLineMapping(factory, 4, 5, '\u3042 \u5b89|\u5b89'); - assertLineMapping(factory, 4, 5, '\u3042\u3042|\u5b89\u5b89'); - assertLineMapping(factory, 4, 5, 'aa |\u5b89)\u5b89|\u5b89'); - assertLineMapping(factory, 4, 5, 'aa \u3042|\u5b89\u3042)|\u5b89'); - assertLineMapping(factory, 4, 5, 'aa |(\u5b89aa|\u5b89'); + test('MonospaceLineBreaksComputer - CJK and Kinsoku Shori', () => { + let factory = new MonospaceLineBreaksComputerFactory('(', '\t)'); + assertLineBreaks(factory, 4, 5, 'aa \u5b89|\u5b89'); + assertLineBreaks(factory, 4, 5, '\u3042 \u5b89|\u5b89'); + assertLineBreaks(factory, 4, 5, '\u3042\u3042|\u5b89\u5b89'); + assertLineBreaks(factory, 4, 5, 'aa |\u5b89)\u5b89|\u5b89'); + assertLineBreaks(factory, 4, 5, 'aa \u3042|\u5b89\u3042)|\u5b89'); + assertLineBreaks(factory, 4, 5, 'aa |(\u5b89aa|\u5b89'); }); - test('CharacterHardWrappingLineMapper - WrappingIndent.Same', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', '\t '); - assertLineMapping(factory, 4, 38, ' *123456789012345678901234567890123456|7890', WrappingIndent.Same); + test('MonospaceLineBreaksComputer - WrappingIndent.Same', () => { + let factory = new MonospaceLineBreaksComputerFactory('', '\t '); + assertLineBreaks(factory, 4, 38, ' *123456789012345678901234567890123456|7890', WrappingIndent.Same); }); test('issue #16332: Scroll bar overlaying on top of text', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', '\t '); - assertLineMapping(factory, 4, 24, 'a/ very/long/line/of/tex|t/that/expands/beyon|d/your/typical/line/|of/code/', WrappingIndent.Indent); + let factory = new MonospaceLineBreaksComputerFactory('', '\t '); + assertLineBreaks(factory, 4, 24, 'a/ very/long/line/of/tex|t/that/expands/beyon|d/your/typical/line/|of/code/', WrappingIndent.Indent); }); test('issue #35162: wrappingIndent not consistently working', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', '\t '); - let mapper = assertLineMapping(factory, 4, 24, ' t h i s |i s |a l |o n |g l |i n |e', WrappingIndent.Indent); + let factory = new MonospaceLineBreaksComputerFactory('', '\t '); + let mapper = assertLineBreaks(factory, 4, 24, ' t h i s |i s |a l |o n |g l |i n |e', WrappingIndent.Indent); assert.equal(mapper!.wrappedTextIndentLength, ' '.length); }); test('issue #75494: surrogate pairs', () => { - let factory = new CharacterHardWrappingLineMapperFactory('\t', ' '); - assertLineMapping(factory, 4, 49, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼|🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼|🐇👬', WrappingIndent.Same); + let factory = new MonospaceLineBreaksComputerFactory('\t', ' '); + assertLineBreaks(factory, 4, 49, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼|🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼|🐇👬', WrappingIndent.Same); }); test('issue #75494: surrogate pairs overrun 1', () => { - const factory = new CharacterHardWrappingLineMapperFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); - assertLineMapping(factory, 4, 4, '🐇👬|&|🌞🌖', WrappingIndent.Same); + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); + assertLineBreaks(factory, 4, 4, '🐇👬|&|🌞🌖', WrappingIndent.Same); }); test('issue #75494: surrogate pairs overrun 2', () => { - const factory = new CharacterHardWrappingLineMapperFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); - assertLineMapping(factory, 4, 17, 'factory, |"xtxtfunc|(x"🌞🏇🍼🌞🏇🍼🐇|&👬🌖🌞👬🌖🌞🏇🍼|🐇👬x"', WrappingIndent.Same); + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); + assertLineBreaks(factory, 4, 17, 'factory, |"xtxtfunc|(x"🌞🏇🍼🌞🏇🍼🐇|&👬🌖🌞👬🌖🌞🏇🍼|🐇👬x"', WrappingIndent.Same); }); - test('CharacterHardWrappingLineMapper - WrappingIndent.DeepIndent', () => { - let factory = new CharacterHardWrappingLineMapperFactory('', '\t '); - let mapper = assertLineMapping(factory, 4, 26, ' W e A r e T e s t |i n g D e |e p I n d |e n t a t |i o n', WrappingIndent.DeepIndent); + test('MonospaceLineBreaksComputer - WrappingIndent.DeepIndent', () => { + let factory = new MonospaceLineBreaksComputerFactory('', '\t '); + let mapper = assertLineBreaks(factory, 4, 26, ' W e A r e T e s t |i n g D e |e p I n d |e n t a t |i o n', WrappingIndent.DeepIndent); assert.equal(mapper!.wrappedTextIndentLength, ' '.length); }); }); diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index ba4228ce9a6..7209c90e881 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -13,8 +13,8 @@ import { EndOfLinePreference } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import * as modes from 'vs/editor/common/modes'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; -import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; -import { LineBreakingData, ISimpleModel, SplitLine, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; +import { LineBreakData, ISimpleModel, SplitLine, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -95,7 +95,7 @@ suite('Editor ViewModel - SplitLinesCollection', () => { const wordWrapBreakBeforeCharacters = config.options.get(EditorOption.wordWrapBreakBeforeCharacters); const wrappingIndent = config.options.get(EditorOption.wrappingIndent); - const hardWrappingLineMapperFactory = new CharacterHardWrappingLineMapperFactory( + const lineBreaksComputerFactory = new MonospaceLineBreaksComputerFactory( wordWrapBreakBeforeCharacters, wordWrapBreakAfterCharacters ); @@ -111,7 +111,7 @@ suite('Editor ViewModel - SplitLinesCollection', () => { const linesCollection = new SplitLinesCollection( model, - hardWrappingLineMapperFactory, + lineBreaksComputerFactory, model.getOptions().tabSize, wrappingInfo.wrappingColumn, fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth, @@ -747,7 +747,7 @@ suite('SplitLinesCollection', () => { const wordWrapBreakBeforeCharacters = configuration.options.get(EditorOption.wordWrapBreakBeforeCharacters); const wrappingIndent = configuration.options.get(EditorOption.wrappingIndent); - const factory = new CharacterHardWrappingLineMapperFactory( + const factory = new MonospaceLineBreaksComputerFactory( wordWrapBreakBeforeCharacters, wordWrapBreakAfterCharacters ); @@ -773,15 +773,15 @@ function pos(lineNumber: number, column: number): Position { } function createSplitLine(splitLengths: number[], breakingOffsetsVisibleColumn: number[], wrappedTextIndentWidth: number, isVisible: boolean = true): SplitLine { - return new SplitLine(createLineMapping(splitLengths, breakingOffsetsVisibleColumn, wrappedTextIndentWidth), isVisible); + return new SplitLine(createLineBreakData(splitLengths, breakingOffsetsVisibleColumn, wrappedTextIndentWidth), isVisible); } -function createLineMapping(breakingLengths: number[], breakingOffsetsVisibleColumn: number[], wrappedTextIndentWidth: number): LineBreakingData { +function createLineBreakData(breakingLengths: number[], breakingOffsetsVisibleColumn: number[], wrappedTextIndentWidth: number): LineBreakData { let sums: number[] = []; for (let i = 0; i < breakingLengths.length; i++) { sums[i] = (i > 0 ? sums[i - 1] : 0) + breakingLengths[i]; } - return new LineBreakingData(sums, breakingOffsetsVisibleColumn, wrappedTextIndentWidth); + return new LineBreakData(sums, breakingOffsetsVisibleColumn, wrappedTextIndentWidth); } function createModel(text: string): ISimpleModel { diff --git a/src/vs/editor/test/common/viewModel/testViewModel.ts b/src/vs/editor/test/common/viewModel/testViewModel.ts index 6513d075b5e..fffa9f05aa9 100644 --- a/src/vs/editor/test/common/viewModel/testViewModel.ts +++ b/src/vs/editor/test/common/viewModel/testViewModel.ts @@ -7,7 +7,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { TextModel } from 'vs/editor/common/model/textModel'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; -import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; +import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; export function testViewModel(text: string[], options: IEditorOptions, callback: (viewModel: ViewModel, model: TextModel) => void): void { const EDITOR_ID = 1; @@ -16,7 +16,7 @@ export function testViewModel(text: string[], options: IEditorOptions, callback: let model = TextModel.createFromString(text.join('\n')); - let viewModel = new ViewModel(EDITOR_ID, configuration, model, CharacterHardWrappingLineMapperFactory.create(configuration.options), null!); + let viewModel = new ViewModel(EDITOR_ID, configuration, model, MonospaceLineBreaksComputerFactory.create(configuration.options), null!); callback(viewModel, model); From 6411ea680afeb8e853137bcf925e0cff990ab588 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 10:41:00 +0100 Subject: [PATCH 108/843] add keyboard command to check/uncheck changes and to apply changes --- .../bulkEdit/browser/bulkEdit.contribution.ts | 58 +++++++++++++++---- .../contrib/bulkEdit/browser/bulkEditPane.ts | 31 ++++++++-- .../contrib/bulkEdit/browser/bulkEditTree.ts | 18 ++---- 3 files changed, 78 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index 19e206bf2c6..e87a536c82f 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -14,14 +14,29 @@ import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewCon import { localize } from 'vs/nls'; import { ViewPaneContainer, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { PaneCompositePanel } from 'vs/workbench/browser/panel'; -import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { RawContextKey, IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { BulkEditPreviewProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; + +function getBulkEditPane(panelService: IPanelService): BulkEditPane | undefined { + let view: ViewPane | undefined; + const activePanel = panelService.openPanel(BulkEditPane.ID, true); + if (activePanel instanceof PaneCompositePanel) { + view = activePanel.getViewPaneContainer().getView(BulkEditPane.ID); + } + if (view instanceof BulkEditPane) { + return view; + } + return undefined; +} class BulkEditPreviewContribution { - static readonly ctxEnabled = new RawContextKey('refactorPreviewIsEnabled', false); + static readonly ctxEnabled = new RawContextKey('refactorPreview.enabled', false); private readonly _ctxEnabled: IContextKey; @@ -40,14 +55,8 @@ class BulkEditPreviewContribution { const oldActivePanel = this._panelService.getActivePanel(); try { - - let view: ViewPane | undefined; - const activePanel = this._panelService.openPanel(BulkEditPane.ID, true); - if (activePanel instanceof PaneCompositePanel) { - view = activePanel.getViewPaneContainer().getView(BulkEditPane.ID); - } - - if (!(view instanceof BulkEditPane)) { + const view = getBulkEditPane(this._panelService); + if (!view) { return edit; } @@ -86,11 +95,38 @@ class BulkEditPreviewContribution { } } +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'refactorPreview.apply', + weight: KeybindingWeight.WorkbenchContrib, + when: BulkEditPreviewContribution.ctxEnabled, + primary: KeyMod.Shift + KeyCode.Enter, + handler(accessor) { + const panelService = accessor.get(IPanelService); + const view = getBulkEditPane(panelService); + if (view) { + view.accept(); + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'refactorPreview.toggleCheckedState', + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(BulkEditPreviewContribution.ctxEnabled, WorkbenchListFocusContextKey), + primary: KeyCode.Space, + handler(accessor) { + const panelService = accessor.get(IPanelService); + const view = getBulkEditPane(panelService); + if (view) { + view.toggleChecked(); + } + } +}); + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( BulkEditPreviewContribution, LifecyclePhase.Ready ); - const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: BulkEditPane.ID, name: localize('panel', "Refactor Preview"), diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 11ab40d84e2..8bdaed6bb88 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -38,8 +38,8 @@ export class BulkEditPane extends ViewPane { private _tree!: WorkbenchAsyncDataTree; private _message!: HTMLSpanElement; - private readonly _acceptAction = new Action('ok', localize('ok', "Apply Refactoring"), 'codicon-check', false, async () => this._done(true)); - private readonly _discardAction = new Action('discard', localize('discard', "Discard"), 'codicon-trash', false, async () => this._done(false)); + private readonly _acceptAction = new Action('ok', localize('ok', "Apply Refactoring"), 'codicon-check', false, async () => this.accept()); + private readonly _discardAction = new Action('discard', localize('discard', "Discard"), 'codicon-trash', false, async () => this.discard()); private readonly _disposables = new DisposableStore(); private readonly _sessionDisposables = new DisposableStore(); @@ -149,9 +149,29 @@ export class BulkEditPane extends ViewPane { if (first instanceof FileElement) { this._tree.expand(first); } + + // refresh when check state changes + this._sessionDisposables.add(input.onDidChangeCheckedState(() => { + this._tree.updateChildren(); + })); }); } + accept(): void { + this._done(true); + } + + discard() { + this._done(false); + } + + toggleChecked() { + const [first] = this._tree.getFocus(); + if (first) { + first.edit.updateChecked(!first.edit.isChecked()); + } + } + private _done(accept: boolean): void { this._setState(State.Message); this._sessionDisposables.clear(); @@ -180,10 +200,9 @@ export class BulkEditPane extends ViewPane { rightResource: previewUri, label: localize('edt.title', "{0} (Refactor Preview)", this._labelService.getUriLabel(element.parent.uri)), options: { - selection: element.edit.edit.range - // preserveFocus, - // pinned, - // revealIfVisible: true + selection: element.edit.edit.range, + revealInCenterIfOutsideViewport: true, + preserveFocus: true } }); } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index c7d86541996..67e5cd70388 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -139,30 +139,26 @@ export class BulkEditIdentityProvider implements IIdentityProvider { - if (this._element) { - this._element.edit.updateChecked(_checkbox.checked); - } - })); this._disposables.add(attachCheckboxStyler(_checkbox, themeService)); } dispose(): void { - this._element = undefined; + this._localDisposables.dispose(); this._disposables.dispose(); this._checkbox.dispose(); this._label.dispose(); } set(element: FileElement, score: FuzzyScore | undefined) { - this._element = element; + this._localDisposables.clear(); + this._localDisposables.add(this._checkbox.onChange(() => element.edit.updateChecked(this._checkbox.checked))); this._checkbox.checked = element.edit.isChecked(); const extraClasses: string[] = []; @@ -243,9 +239,8 @@ class TextEditElementTemplate { set(element: TextEditElement) { this._localDisposables.clear(); this._localDisposables.add(this._checkbox.onChange(() => element.edit.updateChecked(this._checkbox.checked))); - this._localDisposables.add(element.edit.parent.parent.onDidChangeCheckedState(() => { - dom.toggleClass(this._checkbox.domNode, 'disabled', !element.edit.parent.isChecked()); - })); + this._checkbox.checked = element.edit.isChecked(); + dom.toggleClass(this._checkbox.domNode, 'disabled', !element.edit.parent.isChecked()); let value = ''; value += element.prefix; @@ -257,7 +252,6 @@ class TextEditElementTemplate { let insertHighlight: IHighlight = { start: selectHighlight.end, end: selectHighlight.end + element.inserting.length, extraClasses: 'insert' }; this._label.set(value, [selectHighlight, insertHighlight], undefined, true); - this._checkbox.checked = element.edit.isChecked(); } } From 9298d3b4b8ffc1c98f8c64a79915f8d15230a171 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 10 Jan 2020 10:48:53 +0100 Subject: [PATCH 109/843] Fixes #33366: Avoid breaking before a space --- .../viewModel/monospaceLineBreaksComputer.ts | 19 +++++++++++-------- .../monospaceLineBreaksComputer.test.ts | 7 ++++++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts index be52ca2265a..dfec62fc04f 100644 --- a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts +++ b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts @@ -165,7 +165,7 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); } - if (canBreak(prevCharCodeClass, charCodeClass)) { + if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) { breakOffset = charStartOffset; breakOffsetVisibleColumn = visibleColumn; } @@ -243,7 +243,7 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla break; } - if (canBreak(prevCharCodeClass, charCodeClass)) { + if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) { breakOffset = charStartOffset; breakOffsetVisibleColumn = visibleColumn; break; @@ -370,7 +370,7 @@ function createLineBreaks(classifier: WrappingCharacterClassifier, lineText: str charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); } - if (canBreak(prevCharCodeClass, charCodeClass)) { + if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) { breakOffset = charStartOffset; breakOffsetVisibleColumn = visibleColumn; } @@ -427,12 +427,15 @@ function tabCharacterWidth(visibleColumn: number, tabSize: number): number { * Kinsoku Shori : Don't break after a leading character, like an open bracket * Kinsoku Shori : Don't break before a trailing character, like a period */ -function canBreak(prevCharCodeClass: CharacterClass, charCodeClass: CharacterClass): boolean { +function canBreak(prevCharCode: number, prevCharCodeClass: CharacterClass, charCode: number, charCodeClass: CharacterClass): boolean { return ( - (prevCharCodeClass === CharacterClass.BREAK_AFTER) - || (prevCharCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && charCodeClass !== CharacterClass.BREAK_AFTER) - || (charCodeClass === CharacterClass.BREAK_BEFORE) - || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && prevCharCodeClass !== CharacterClass.BREAK_BEFORE) + charCode !== CharCode.Space + && ( + (prevCharCodeClass === CharacterClass.BREAK_AFTER) + || (prevCharCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && charCodeClass !== CharacterClass.BREAK_AFTER) + || (charCodeClass === CharacterClass.BREAK_BEFORE) + || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && prevCharCodeClass !== CharacterClass.BREAK_BEFORE) + ) ); } diff --git a/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts b/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts index 625c678ff47..a95095bbc99 100644 --- a/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts +++ b/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts @@ -146,7 +146,7 @@ suite('Editor ViewModel - MonospaceLineBreaksComputer', () => { assertIncrementalLineBreaks( factory, 'An his legere persecuti, oblique delicata efficiantur ex vix, vel at graecis officiis maluisset. Et per impedit voluptua, usu discere maiorum at. Ut assum ornatus temporibus vis, an sea melius pericula. Ea dicunt oblique phaedrum nam, eu duo movet nobis. His melius facilis eu, vim malorum temporibus ne. Nec no sale regione, meliore civibus placerat id eam. Mea alii fabulas definitionem te, agam volutpat ad vis, et per bonorum nonumes repudiandae.', 4, 57, 'An his legere persecuti, oblique delicata efficiantur ex |vix, vel at graecis officiis maluisset. Et per impedit |voluptua, usu discere maiorum at. Ut assum ornatus |temporibus vis, an sea melius pericula. Ea dicunt |oblique phaedrum nam, eu duo movet nobis. His melius |facilis eu, vim malorum temporibus ne. Nec no sale |regione, meliore civibus placerat id eam. Mea alii |fabulas definitionem te, agam volutpat ad vis, et per |bonorum nonumes repudiandae.', - 58, 'An his legere persecuti, oblique delicata efficiantur ex |vix, vel at graecis officiis maluisset. Et per impedit |voluptua, usu discere maiorum at. Ut assum ornatus |temporibus vis, an sea melius pericula. Ea dicunt oblique |phaedrum nam, eu duo movet nobis. His melius facilis eu, |vim malorum temporibus ne. Nec no sale regione, meliore |civibus placerat id eam. Mea alii fabulas definitionem te,| agam volutpat ad vis, et per bonorum nonumes repudiandae.' + 58, 'An his legere persecuti, oblique delicata efficiantur ex |vix, vel at graecis officiis maluisset. Et per impedit |voluptua, usu discere maiorum at. Ut assum ornatus |temporibus vis, an sea melius pericula. Ea dicunt oblique |phaedrum nam, eu duo movet nobis. His melius facilis eu, |vim malorum temporibus ne. Nec no sale regione, meliore |civibus placerat id eam. Mea alii fabulas definitionem |te, agam volutpat ad vis, et per bonorum nonumes |repudiandae.' ); assertIncrementalLineBreaks( @@ -232,4 +232,9 @@ suite('Editor ViewModel - MonospaceLineBreaksComputer', () => { let mapper = assertLineBreaks(factory, 4, 26, ' W e A r e T e s t |i n g D e |e p I n d |e n t a t |i o n', WrappingIndent.DeepIndent); assert.equal(mapper!.wrappedTextIndentLength, ' '.length); }); + + test('issue #33366: Word wrap algorithm behaves differently around punctuation', () => { + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); + assertLineBreaks(factory, 4, 23, 'this is a line of |text, text that sits |on a line', WrappingIndent.Same); + }); }); From 25275a298119c745628b402a8e1a70a0d8975489 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 10 Jan 2020 10:53:17 +0100 Subject: [PATCH 110/843] Fix port labeling and localhost candidate display --- .../remote/common/remoteExplorerService.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/remote/common/remoteExplorerService.ts b/src/vs/workbench/services/remote/common/remoteExplorerService.ts index cfc38fbed3b..fe35ca3b9f5 100644 --- a/src/vs/workbench/services/remote/common/remoteExplorerService.ts +++ b/src/vs/workbench/services/remote/common/remoteExplorerService.ts @@ -45,11 +45,15 @@ export interface Tunnel { closeable?: boolean; } -export function MakeAddress(host: string, port: number): string { +function ToLocalHost(host: string): string { if (host === '127.0.0.1') { host = 'localhost'; } - return host + ':' + port; + return host; +} + +export function MakeAddress(host: string, port: number): string { + return ToLocalHost(host) + ':' + port; } export class TunnelModel extends Disposable { @@ -201,7 +205,13 @@ export class TunnelModel extends Disposable { return; } if (this._candidateFinder) { - this._candidates = await this._candidateFinder(); + this._candidates = (await this._candidateFinder()).map(value => { + return { + host: ToLocalHost(value.host), + port: value.port, + detail: value.detail + }; + }); } } @@ -286,7 +296,9 @@ class RemoteExplorerService implements IRemoteExplorerService { } getEditableData(tunnelItem: ITunnelItem | undefined): IEditableData | undefined { - return (this._editable && (!tunnelItem || (this._editable.tunnelItem?.remotePort === tunnelItem.remotePort) && (this._editable.tunnelItem.remoteHost === tunnelItem.remoteHost))) ? + return (this._editable && + ((!tunnelItem && (tunnelItem === this._editable.tunnelItem)) || + (tunnelItem && (this._editable.tunnelItem?.remotePort === tunnelItem.remotePort) && (this._editable.tunnelItem.remoteHost === tunnelItem.remoteHost)))) ? this._editable.data : undefined; } From 0398618f452acd0bd9f057d1d9efacec75d6a3eb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 10 Jan 2020 11:15:03 +0100 Subject: [PATCH 111/843] Allow to save readonly documents (fixes #22121) --- .../editor/common/services/resolverService.ts | 7 +- .../standalone/browser/simpleServices.ts | 4 + .../common/editor/resourceEditorInput.ts | 31 ++++++- .../contrib/output/browser/logViewer.ts | 10 ++- .../electron-browser/perfviewEditor.ts | 10 ++- .../common/preferencesEditorInput.ts | 11 ++- .../textfile/browser/textFileService.ts | 87 +++++++++++-------- .../electron-browser/nativeTextFileService.ts | 6 +- .../common/textModelResolverService.ts | 4 + .../workbench/test/workbenchTestServices.ts | 6 +- 10 files changed, 123 insertions(+), 53 deletions(-) diff --git a/src/vs/editor/common/services/resolverService.ts b/src/vs/editor/common/services/resolverService.ts index 77719683a23..236bc5ef3ff 100644 --- a/src/vs/editor/common/services/resolverService.ts +++ b/src/vs/editor/common/services/resolverService.ts @@ -53,9 +53,14 @@ export interface ITextEditorModel extends IEditorModel { createSnapshot(this: ITextEditorModel): ITextSnapshot | null; /** - * Signals if this model is readonly or not. + * Signals if this model is readonly or not. */ isReadonly(): boolean; + + /** + * Figure out if this model is resolved or not. + */ + isResolved(): this is IResolvedTextEditorModel; } export interface IResolvedTextEditorModel extends ITextEditorModel { diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index b5180aa9a2a..409a1dfeb52 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -80,6 +80,10 @@ export class SimpleModel implements IResolvedTextEditorModel { public dispose(): void { this._onDispose.fire(); } + + public isResolved(): boolean { + return true; + } } export interface IOpenEditorDelegate { diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index ee9605889c4..c7f8bbbcb3c 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -3,12 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditorInput, ITextEditorModel, IModeSupport } from 'vs/workbench/common/editor'; +import { EditorInput, ITextEditorModel, IModeSupport, GroupIdentifier, isTextEditor } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { IReference } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { basename } from 'vs/base/common/resources'; +import { ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import type { IEditorViewState } from 'vs/editor/common/editorCommon'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; /** * A read-only text editor input whos contents are made of the provided resource that points to an existing @@ -26,7 +29,9 @@ export class ResourceEditorInput extends EditorInput implements IModeSupport { private description: string | undefined, private readonly resource: URI, private preferredMode: string | undefined, - @ITextModelService private readonly textModelResolverService: ITextModelService + @ITextModelService private readonly textModelResolverService: ITextModelService, + @ITextFileService private readonly textFileService: ITextFileService, + @IEditorService private readonly editorService: IEditorService ) { super(); @@ -104,6 +109,28 @@ export class ResourceEditorInput extends EditorInput implements IModeSupport { return model; } + async saveAs(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { + + // 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(); + } + + // Save as + const target = await this.textFileService.saveAs(this.resource, undefined, options); + if (!target) { + return false; // save cancelled + } + + // Open the target + await this.editorService.openEditor({ resource: target, options: { viewState, pinned: true } }, group); + + return true; + } + matches(otherInput: unknown): boolean { if (super.matches(otherInput) === true) { return true; diff --git a/src/vs/workbench/contrib/output/browser/logViewer.ts b/src/vs/workbench/contrib/output/browser/logViewer.ts index f672f55cac2..dbe437aff0c 100644 --- a/src/vs/workbench/contrib/output/browser/logViewer.ts +++ b/src/vs/workbench/contrib/output/browser/logViewer.ts @@ -18,15 +18,19 @@ import { LOG_SCHEME } from 'vs/workbench/contrib/output/common/output'; import { IFileOutputChannelDescriptor } from 'vs/workbench/services/output/common/output'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; export class LogViewerInput extends ResourceEditorInput { static readonly ID = 'workbench.editorinputs.output'; - constructor(private readonly outputChannelDescriptor: IFileOutputChannelDescriptor, - @ITextModelService textModelResolverService: ITextModelService + constructor( + private readonly outputChannelDescriptor: IFileOutputChannelDescriptor, + @ITextModelService textModelResolverService: ITextModelService, + @ITextFileService textFileService: ITextFileService, + @IEditorService editorService: IEditorService ) { - super(basename(outputChannelDescriptor.file.path), dirname(outputChannelDescriptor.file.path), URI.from({ scheme: LOG_SCHEME, path: outputChannelDescriptor.id }), undefined, textModelResolverService); + super(basename(outputChannelDescriptor.file.path), dirname(outputChannelDescriptor.file.path), URI.from({ scheme: LOG_SCHEME, path: outputChannelDescriptor.id }), undefined, textModelResolverService, textFileService, editorService); } getTypeId(): string { diff --git a/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts index c9452a33c10..31ab953bff3 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts @@ -21,6 +21,8 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { writeTransientState } from 'vs/workbench/contrib/codeEditor/browser/toggleWordWrap'; import { mergeSort } from 'vs/base/common/arrays'; import product from 'vs/platform/product/common/product'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export class PerfviewContrib { @@ -44,14 +46,18 @@ export class PerfviewInput extends ResourceEditorInput { static readonly Uri = URI.from({ scheme: 'perf', path: 'Startup Performance' }); constructor( - @ITextModelService textModelResolverService: ITextModelService + @ITextModelService textModelResolverService: ITextModelService, + @ITextFileService textFileService: ITextFileService, + @IEditorService editorService: IEditorService ) { super( localize('name', "Startup Performance"), undefined, PerfviewInput.Uri, undefined, - textModelResolverService + textModelResolverService, + textFileService, + editorService ); } diff --git a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts index 0b4e5a69d79..501a673894f 100644 --- a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts +++ b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts @@ -13,6 +13,8 @@ import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorIn import { KeybindingsEditorModel } from 'vs/workbench/services/preferences/common/keybindingsEditorModel'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export class PreferencesEditorInput extends SideBySideEditorInput { static readonly ID: string = 'workbench.editorinputs.preferencesEditorInput'; @@ -28,10 +30,13 @@ export class PreferencesEditorInput extends SideBySideEditorInput { export class DefaultPreferencesEditorInput extends ResourceEditorInput { static readonly ID = 'workbench.editorinputs.defaultpreferences'; - constructor(defaultSettingsResource: URI, - @ITextModelService textModelResolverService: ITextModelService + constructor( + defaultSettingsResource: URI, + @ITextModelService textModelResolverService: ITextModelService, + @ITextFileService textFileService: ITextFileService, + @IEditorService editorService: IEditorService ) { - super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, undefined, textModelResolverService); + super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, undefined, textModelResolverService, textFileService, editorService); } getTypeId(): string { diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 93658cabb39..0c0d2cad011 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -9,7 +9,7 @@ import { Emitter, AsyncEmitter } from 'vs/base/common/event'; import * as platform from 'vs/base/common/platform'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, FileOperationWillRunEvent, FileOperationDidRunEvent, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; -import { SaveReason, IRevertOptions } from 'vs/workbench/common/editor'; +import { SaveReason, IRevertOptions, IEncodingSupport } from 'vs/workbench/common/editor'; import { ILifecycleService, ShutdownReason, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileService, FileOperationError, FileOperationResult, HotExitConfiguration, IFileStatWithMetadata, ICreateFileOptions, FileOperation } from 'vs/platform/files/common/files'; @@ -37,6 +37,7 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/tex import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; /** * The workbench file service implementation implements the raw file service spec and adds additional methods on top. @@ -76,7 +77,8 @@ export abstract class AbstractTextFileService extends Disposable implements ITex @IFileDialogService private readonly fileDialogService: IFileDialogService, @IEditorService private readonly editorService: IEditorService, @ITextResourceConfigurationService protected readonly textResourceConfigurationService: ITextResourceConfigurationService, - @IFilesConfigurationService protected readonly filesConfigurationService: IFilesConfigurationService + @IFilesConfigurationService protected readonly filesConfigurationService: IFilesConfigurationService, + @ITextModelService private readonly textModelService: ITextModelService ) { super(); @@ -676,68 +678,77 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return this.getFileModels(resources).filter(model => model.isDirty()); } - async saveAs(resource: URI, targetResource?: URI, options?: ITextFileSaveOptions): Promise { + async saveAs(source: URI, target?: URI, options?: ITextFileSaveOptions): Promise { // Get to target resource - if (!targetResource) { - let dialogPath = resource; - if (resource.scheme === Schemas.untitled) { - dialogPath = this.suggestFileName(resource); + if (!target) { + let dialogPath = source; + if (source.scheme === Schemas.untitled) { + dialogPath = this.suggestFileName(source); } - targetResource = await this.promptForPath(resource, dialogPath, options ? options.availableFileSystems : undefined); + target = await this.promptForPath(source, dialogPath, options ? options.availableFileSystems : undefined); } - if (!targetResource) { + if (!target) { return; // user canceled } // Just save if target is same as models own resource - if (resource.toString() === targetResource.toString()) { - await this.save(resource, options); + if (source.toString() === target.toString()) { + await this.save(source, options); - return resource; + return source; } // Do it - return this.doSaveAs(resource, targetResource, options); + return this.doSaveAs(source, target, options); } - private async doSaveAs(resource: URI, target: URI, options?: ITextFileSaveOptions): Promise { + private async doSaveAs(source: URI, target: URI, options?: ITextFileSaveOptions): Promise { + let success = false; - // Retrieve text model from provided resource if any - let model: ITextFileEditorModel | UntitledTextEditorModel | undefined; - if (this.fileService.canHandleResource(resource)) { - model = this._models.get(resource); - } else if (resource.scheme === Schemas.untitled && this.untitledTextEditorService.exists(resource)) { - model = await this.untitledTextEditorService.createOrGet({ resource }).resolve(); + // If the source is an existing text file model, we can directly + // use that model to copy the contents to the target destination + const textFileModel = this._models.get(source); + if (textFileModel && textFileModel.isResolved()) { + success = await this.doSaveAsTextFile(textFileModel, source, target, options); } - // We have a model: Use it (can be null e.g. if this file is binary and not a text file or was never opened before) - let result: boolean; - if (model) { - result = await this.doSaveTextFileAs(model, resource, target, options); + // Otherwise if the source can be handled by the file service + // we can simply invoke the copy() function to save as + else if (this.fileService.canHandleResource(source)) { + await this.fileService.copy(source, target); + + success = true; } - // Otherwise we can only copy - else { - await this.fileService.copy(resource, target); + // Finally, if the source does not seem to be a file, we have to + // try to resolve a text model from the resource to get at the + // contents and additional meta data (e.g. encoding). + else if (this.textModelService.hasTextModelContentProvider(source.scheme)) { + const modelReference = await this.textModelService.createModelReference(source); + success = await this.doSaveAsTextFile(modelReference.object, source, target, options); - result = true; + modelReference.dispose(); // free up our use of the reference } - // Return early if the operation was not running - if (!result) { - return target; + // Revert the source if result is success + if (success) { + await this.revert(source); } - // Revert the source - await this.revert(resource); - return target; } - private async doSaveTextFileAs(sourceModel: ITextFileEditorModel | UntitledTextEditorModel, resource: URI, target: URI, options?: ITextFileSaveOptions): Promise { + private async doSaveAsTextFile(sourceModel: IResolvedTextEditorModel, source: URI, target: URI, options?: ITextFileSaveOptions): Promise { + + // Find source encoding if any + let sourceModelEncoding: string | undefined = undefined; + const sourceModelWithEncodingSupport = (sourceModel as unknown as IEncodingSupport); + if (typeof sourceModelWithEncodingSupport.getEncoding === 'function') { + sourceModelEncoding = sourceModelWithEncodingSupport.getEncoding(); + } // Prefer an existing model if it is already loaded for the given target resource let targetExists: boolean = false; @@ -764,7 +775,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex } } - targetModel = await this.models.loadOrCreate(target, { encoding: sourceModel.getEncoding(), mode }); + targetModel = await this.models.loadOrCreate(target, { encoding: sourceModelEncoding, mode }); } try { @@ -785,7 +796,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex } // take over model value, encoding and mode (only if more specific) from source model - targetModel.updatePreferredEncoding(sourceModel.getEncoding()); + targetModel.updatePreferredEncoding(sourceModelEncoding); if (sourceModel.isResolved() && targetModel.isResolved()) { this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot())); @@ -809,7 +820,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex ) { await this.fileService.del(target); - return this.doSaveTextFileAs(sourceModel, resource, target, options); + return this.doSaveAsTextFile(sourceModel, source, target, options); } throw error; diff --git a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts index 5e34b6d4c82..17eeb9d9228 100644 --- a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts @@ -41,6 +41,7 @@ import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/d import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { assign } from 'vs/base/common/objects'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; export class NativeTextFileService extends AbstractTextFileService { @@ -62,9 +63,10 @@ export class NativeTextFileService extends AbstractTextFileService { @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, @IElectronService private readonly electronService: IElectronService, @IProductService private readonly productService: IProductService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService + @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, + @ITextModelService textModelService: ITextModelService ) { - super(contextService, fileService, untitledTextEditorService, lifecycleService, instantiationService, modeService, modelService, environmentService, notificationService, backupFileService, historyService, dialogService, fileDialogService, editorService, textResourceConfigurationService, filesConfigurationService); + super(contextService, fileService, untitledTextEditorService, lifecycleService, instantiationService, modeService, modelService, environmentService, notificationService, backupFileService, historyService, dialogService, fileDialogService, editorService, textResourceConfigurationService, filesConfigurationService, textModelService); } private _encoding: EncodingOracle | undefined; diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index 2b630820d7f..be9f2a32793 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -175,6 +175,10 @@ export class TextModelResolverService implements ITextModelService { } hasTextModelContentProvider(scheme: string): boolean { + if (scheme === network.Schemas.untitled || scheme === network.Schemas.inMemory) { + return true; // we handle untitled:// and inMemory:// within + } + return this.resourceModelCollection.hasTextModelContentProvider(scheme); } } diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 868e183d45c..c9b8e6f48d7 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -213,7 +213,8 @@ export class TestTextFileService extends NativeTextFileService { @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, @IElectronService electronService: IElectronService, @IProductService productService: IProductService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService + @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, + @ITextModelService textModelService: ITextModelService ) { super( contextService, @@ -233,7 +234,8 @@ export class TestTextFileService extends NativeTextFileService { textResourceConfigurationService, electronService, productService, - filesConfigurationService + filesConfigurationService, + textModelService ); } From 8bbfbfff1921d2589ce7f5c279bd7a54d3ea5b08 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 10 Jan 2020 11:18:01 +0100 Subject: [PATCH 112/843] fixes #88342 --- src/vs/workbench/contrib/files/browser/fileActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 87a82bf8b7c..3774785acbc 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -202,9 +202,9 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi let { message, detail } = getMoveToTrashMessage(distinctElements); detail += detail ? '\n' : ''; if (isWindows) { - detail += nls.localize('undoBin', "You can restore from the Recycle Bin."); + detail += distinctElements.length > 1 ? nls.localize('undoBinFiles', "You can restore these files from the Recycle Bin.") : nls.localize('undoBin', "You can restore this file from the Recycle Bin."); } else { - detail += nls.localize('undoTrash', "You can restore from the Trash."); + detail += distinctElements.length > 1 ? nls.localize('undoTrashFiles', "You can restore these files from the Trash.") : nls.localize('undoTrash', "You can restore this file from the Trash."); } confirmDeletePromise = dialogService.confirm({ From 13606845f5624de5f54fef6705c2c356b18cd199 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 11:35:54 +0100 Subject: [PATCH 113/843] fix issue when having an empty text edit --- src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 5808f9a78fd..97140519b5d 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -198,7 +198,7 @@ class BulkEditModel implements IDisposable { for (const edit of value) { if (makeMinimal) { const newEdits = await this._editorWorker.computeMoreMinimalEdits(edit.resource, edit.edits); - task.addEdit({ ...edit, edits: newEdits! }); + task.addEdit({ ...edit, edits: newEdits ?? edit.edits }); } else { task.addEdit(edit); From b4136b65c6919b97272d2c098cdb5a7f59f5b3dd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 11:49:31 +0100 Subject: [PATCH 114/843] use native checkbox for consistency --- .../contrib/bulkEdit/browser/bulkEdit.css | 15 ---- .../contrib/bulkEdit/browser/bulkEditPane.ts | 9 ++- .../contrib/bulkEdit/browser/bulkEditTree.ts | 76 ++++++++----------- 3 files changed, 38 insertions(+), 62 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css index e23be66097c..3cd76dc760f 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css @@ -28,24 +28,9 @@ } .monaco-workbench .bulk-edit-panel .monaco-tl-contents .edit-checkbox { - height: 16px; - width: 16px; - min-height: 16px; - min-width: 16px; align-self: center; - margin-right: 4px; - border: 1px solid transparent; - border-radius: 2px; } .monaco-workbench .bulk-edit-panel .monaco-tl-contents .edit-checkbox.disabled { opacity: .5; } - -.monaco-workbench .bulk-edit-panel .monaco-tl-contents .edit-checkbox.codicon[class*='codicon-'] { - font-size: 13px; -} - -.monaco-workbench .bulk-edit-panel .monaco-tl-contents .edit-checkbox.codicon:not(.checked)::before { - opacity: 0; -} diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 8bdaed6bb88..b9d1c6ca68b 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -25,6 +25,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; const enum State { Data = 'data', @@ -71,6 +72,12 @@ export class BulkEditPane extends ViewPane { protected renderBody(parent: HTMLElement): void { parent.classList.add('bulk-edit-panel', 'show-file-icons'); + const resourceLabels = this._instaService.createInstance( + ResourceLabels, + { onDidChangeVisibility: this.onDidChangeBodyVisibility } + ); + this._disposables.add(resourceLabels); + // tree const treeContainer = document.createElement('div'); treeContainer.className = 'tree'; @@ -81,7 +88,7 @@ export class BulkEditPane extends ViewPane { this._tree = this._instaService.createInstance( WorkbenchAsyncDataTree, this.id, treeContainer, new BulkEditDelegate(), - [this._instaService.createInstance(TextEditElementRenderer), this._instaService.createInstance(FileElementRenderer)], + [new TextEditElementRenderer(), new FileElementRenderer(resourceLabels)], this._instaService.createInstance(BulkEditDataSource), { identityProvider: new BulkEditIdentityProvider(), diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 67e5cd70388..b83b83dc92b 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -6,9 +6,8 @@ import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; -import { IResourceLabel, ResourceLabels, DEFAULT_LABELS_CONTAINER } from 'vs/workbench/browser/labels'; +import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { URI } from 'vs/base/common/uri'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { Range } from 'vs/editor/common/core/range'; @@ -18,9 +17,6 @@ import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TextModel } from 'vs/editor/common/model/textModel'; import { BulkFileOperations, BulkFileOperation, BulkFileOperationType, BulkTextEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; import { localize } from 'vs/nls'; -import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { attachCheckboxStyler } from 'vs/platform/theme/common/styler'; import { FileKind } from 'vs/platform/files/common/files'; // --- VIEW MODEL @@ -141,24 +137,29 @@ class FileElementTemplate { private readonly _disposables = new DisposableStore(); private readonly _localDisposables = new DisposableStore(); - constructor( - private readonly _checkbox: Checkbox, - private readonly _label: IResourceLabel, - @IThemeService themeService: IThemeService - ) { - this._disposables.add(attachCheckboxStyler(_checkbox, themeService)); + private readonly _checkbox: HTMLInputElement; + private readonly _label: IResourceLabel; + + constructor(container: HTMLElement, resourceLabels: ResourceLabels) { + + this._checkbox = document.createElement('input'); + this._checkbox.className = 'edit-checkbox'; + this._checkbox.type = 'checkbox'; + this._checkbox.setAttribute('role', 'checkbox'); + container.appendChild(this._checkbox); + + this._label = resourceLabels.create(container, { supportHighlights: true }); } dispose(): void { this._localDisposables.dispose(); this._disposables.dispose(); - this._checkbox.dispose(); this._label.dispose(); } set(element: FileElement, score: FuzzyScore | undefined) { this._localDisposables.clear(); - this._localDisposables.add(this._checkbox.onChange(() => element.edit.updateChecked(this._checkbox.checked))); + this._localDisposables.add(dom.addDisposableListener(this._checkbox, 'change', (() => element.edit.updateChecked(this._checkbox.checked)))); this._checkbox.checked = element.edit.isChecked(); const extraClasses: string[] = []; @@ -187,24 +188,10 @@ export class FileElementRenderer implements ITreeRenderer, _index: number, template: FileElementTemplate): void { @@ -221,26 +208,30 @@ class TextEditElementTemplate { private readonly _disposables = new DisposableStore(); private readonly _localDisposables = new DisposableStore(); - constructor( - private readonly _checkbox: Checkbox, - private readonly _label: HighlightedLabel, - @IThemeService themeService: IThemeService - ) { + private readonly _checkbox: HTMLInputElement; + private readonly _label: HighlightedLabel; - this._disposables.add(attachCheckboxStyler(_checkbox, themeService)); + constructor(container: HTMLElement) { + this._checkbox = document.createElement('input'); + this._checkbox.className = 'edit-checkbox'; + this._checkbox.type = 'checkbox'; + this._checkbox.setAttribute('role', 'checkbox'); + container.appendChild(this._checkbox); + + this._label = new HighlightedLabel(container, false); + dom.addClass(this._label.element, 'textedit'); } dispose(): void { this._localDisposables.dispose(); this._disposables.dispose(); - this._checkbox.dispose(); } set(element: TextEditElement) { this._localDisposables.clear(); - this._localDisposables.add(this._checkbox.onChange(() => element.edit.updateChecked(this._checkbox.checked))); + this._localDisposables.add(dom.addDisposableListener(this._checkbox, 'change', () => element.edit.updateChecked(this._checkbox.checked))); this._checkbox.checked = element.edit.isChecked(); - dom.toggleClass(this._checkbox.domNode, 'disabled', !element.edit.parent.isChecked()); + dom.toggleClass(this._checkbox, 'disabled', !element.edit.parent.isChecked()); let value = ''; value += element.prefix; @@ -261,15 +252,8 @@ export class TextEditElementRenderer implements ITreeRenderer, _index: number, template: TextEditElementTemplate): void { From 416433886d16a6a05ace4c8b9edff2a34f1803f1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 10 Jan 2020 12:04:37 +0100 Subject: [PATCH 115/843] files - remove PENDING_AUTO_SAVE model state --- .../contrib/files/common/editors/fileEditorInput.ts | 6 ++++-- .../services/textfile/browser/browserTextFileService.ts | 2 +- .../services/textfile/common/textFileEditorModel.ts | 2 -- src/vs/workbench/services/textfile/common/textfiles.ts | 5 ----- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index b0a38ea9b97..799f816c760 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -69,14 +69,16 @@ export class FileEditorInput extends TextEditorInput implements IFileEditorInput private registerListeners(): void { - // Model changes + // Dirty changes this._register(this.textFileService.models.onModelDirty(e => this.onDirtyStateChange(e))); this._register(this.textFileService.models.onModelSaveError(e => this.onDirtyStateChange(e))); this._register(this.textFileService.models.onModelSaved(e => this.onDirtyStateChange(e))); this._register(this.textFileService.models.onModelReverted(e => this.onDirtyStateChange(e))); - this._register(this.textFileService.models.onModelOrphanedChanged(e => this.onModelOrphanedChanged(e))); + + // Label changes this._register(this.labelService.onDidChangeFormatters(() => FileEditorInput.MEMOIZER.clear())); this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(() => FileEditorInput.MEMOIZER.clear())); + this._register(this.textFileService.models.onModelOrphanedChanged(e => this.onModelOrphanedChanged(e))); } private onDirtyStateChange(e: TextFileModelChangeEvent): void { diff --git a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts index 858552d31c3..37bbc4bc070 100644 --- a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts +++ b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts @@ -26,7 +26,7 @@ export class BrowserTextFileService extends AbstractTextFileService { } private doBeforeShutdownSync(): boolean { - if (this.models.getAll().some(model => model.hasState(ModelState.PENDING_SAVE) || model.hasState(ModelState.PENDING_AUTO_SAVE))) { + if (this.models.getAll().some(model => model.hasState(ModelState.PENDING_SAVE))) { return true; // files are pending to be saved: veto } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 539c595282f..a8458d9f09f 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -953,8 +953,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.inOrphanMode; case ModelState.PENDING_SAVE: return this.saveSequentializer.hasPendingSave(); - case ModelState.PENDING_AUTO_SAVE: - return !!this.autoSaveDisposable.value; case ModelState.SAVED: return !this.dirty; } diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index b7abec08cba..a61a80c21d4 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -257,11 +257,6 @@ export const enum ModelState { */ PENDING_SAVE, - /** - * A model is marked for being saved after a specific timeout. - */ - PENDING_AUTO_SAVE, - /** * A model is in conflict mode when changes cannot be saved because the * underlying file has changed. Models in conflict mode are always dirty. From 358b1ab5af5a61b40c1c06c4c2cff0c583f25dae Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Fri, 10 Jan 2020 16:38:17 +0530 Subject: [PATCH 116/843] skip webview tests for https://github.com/microsoft/vscode/issues/88415 --- .../vscode-api-tests/src/singlefolder-tests/webview.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts index e785f1d4afb..b59d91ff380 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -13,7 +13,8 @@ const webviewId = 'myWebview'; const testDocument = join(vscode.workspace.rootPath || '', './bower.json'); -suite('Webview tests', () => { +// TODO: Re-enable after https://github.com/microsoft/vscode/issues/88415 +suite.skip('Webview tests', () => { const disposables: vscode.Disposable[] = []; function _register(disposable: T) { From 68fadbd08ed01fce4c4c276232d8a8bfe8bbed9d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 12:40:32 +0100 Subject: [PATCH 117/843] prepare for label --- src/vs/base/browser/ui/splitview/paneview.ts | 4 ++-- src/vs/editor/browser/services/bulkEditService.ts | 1 + src/vs/editor/contrib/rename/rename.ts | 3 ++- .../contrib/bulkEdit/browser/bulkEdit.contribution.ts | 8 ++++---- src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css | 8 ++++---- src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts | 8 +++++--- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts index e50add0846a..037ffdce27b 100644 --- a/src/vs/base/browser/ui/splitview/paneview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -44,8 +44,8 @@ export abstract class Pane extends Disposable implements IView { private static readonly HEADER_SIZE = 22; readonly element: HTMLElement; - protected header!: HTMLElement; - protected body!: HTMLElement; + private header!: HTMLElement; + private body!: HTMLElement; protected _expanded: boolean; diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index 0aecb86edfa..d7d1ad70417 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -15,6 +15,7 @@ export interface IBulkEditOptions { editor?: ICodeEditor; progress?: IProgress; showPreview?: boolean; + label?: string; } export interface IBulkEditResult { diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 20ef921a6c1..9f3c10786f6 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -200,7 +200,8 @@ class RenameController implements IEditorContribution { this._bulkEditService.apply(renameResult, { editor: this.editor, - showPreview: inputFieldResult.wantsPreview + showPreview: inputFieldResult.wantsPreview, + label: nls.localize('label', "Renaming '{0}'", loc?.text) }).then(result => { if (result.ariaSummary) { alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, inputFieldResult.newName, result.ariaSummary)); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index e87a536c82f..2e46f5687aa 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -7,7 +7,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { IBulkEditService, IBulkEditOptions } from 'vs/editor/browser/services/bulkEditService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; import { BulkEditPane } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPane'; import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewsRegistry } from 'vs/workbench/common/views'; @@ -46,11 +46,11 @@ class BulkEditPreviewContribution { @IBulkEditService bulkEditService: IBulkEditService, @IContextKeyService contextKeyService: IContextKeyService, ) { - bulkEditService.setPreviewHandler(edit => this._previewEdit(edit)); + bulkEditService.setPreviewHandler((edit, options) => this._previewEdit(edit, options)); this._ctxEnabled = BulkEditPreviewContribution.ctxEnabled.bindTo(contextKeyService); } - private async _previewEdit(edit: WorkspaceEdit) { + private async _previewEdit(edit: WorkspaceEdit, options?: IBulkEditOptions) { this._ctxEnabled.set(true); const oldActivePanel = this._panelService.getActivePanel(); @@ -60,7 +60,7 @@ class BulkEditPreviewContribution { return edit; } - const newEditOrUndefined = await view.setInput(edit); + const newEditOrUndefined = await view.setInput(edit, options?.label); if (!newEditOrUndefined) { return { edits: [] }; } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css index 3cd76dc760f..b83367eb5d1 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css @@ -11,14 +11,14 @@ padding: 10px 20px } -.monaco-workbench .bulk-edit-panel[data-state="message"] .message, -.monaco-workbench .bulk-edit-panel[data-state="data"] .tree +.monaco-workbench .bulk-edit-panel [data-state="message"] .message, +.monaco-workbench .bulk-edit-panel [data-state="data"] .tree { display: inherit; } -.monaco-workbench .bulk-edit-panel[data-state="data"] .message, -.monaco-workbench .bulk-edit-panel[data-state="message"] .tree +.monaco-workbench .bulk-edit-panel [data-state="data"] .message, +.monaco-workbench .bulk-edit-panel [data-state="message"] .tree { display: none; } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index b9d1c6ca68b..510aa68d0ac 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -41,6 +41,7 @@ export class BulkEditPane extends ViewPane { private readonly _acceptAction = new Action('ok', localize('ok', "Apply Refactoring"), 'codicon-check', false, async () => this.accept()); private readonly _discardAction = new Action('discard', localize('discard', "Discard"), 'codicon-trash', false, async () => this.discard()); + private readonly _disposables = new DisposableStore(); private readonly _sessionDisposables = new DisposableStore(); @@ -62,6 +63,8 @@ export class BulkEditPane extends ViewPane { options, keybindingService, contextMenuService, configurationService, contextKeyService ); + + this.element.classList.add('bulk-edit-panel', 'show-file-icons'); } dispose(): void { @@ -70,7 +73,6 @@ export class BulkEditPane extends ViewPane { } protected renderBody(parent: HTMLElement): void { - parent.classList.add('bulk-edit-panel', 'show-file-icons'); const resourceLabels = this._instaService.createInstance( ResourceLabels, @@ -124,10 +126,10 @@ export class BulkEditPane extends ViewPane { } private _setState(state: State): void { - this.body.dataset['state'] = state; + this.element.dataset['state'] = state; } - async setInput(edit: WorkspaceEdit): Promise { + async setInput(edit: WorkspaceEdit, label?: string): Promise { this._setState(State.Data); this._sessionDisposables.clear(); From c5a432c6b64655990b67624c10f82f2d752a0504 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 10 Jan 2020 13:04:28 +0100 Subject: [PATCH 118/843] Fix #88293 --- src/vs/workbench/contrib/search/browser/replaceService.ts | 5 +++-- src/vs/workbench/contrib/search/common/searchModel.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index bc7e14ab2e2..4f962476861 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -121,9 +121,10 @@ export class ReplaceService implements IReplaceService { revealIfVisible: true } }).then(editor => { + const input = editor?.input; const disposable = fileMatch.onDispose(() => { - if (editor && editor.input) { - editor.input.dispose(); + if (input) { + input.dispose(); } disposable.dispose(); }); diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index 1dbbad8b690..7e0db5d1da4 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -543,7 +543,7 @@ export class FolderMatch extends Disposable { replace(match: FileMatch): Promise { return this.replaceService.replace([match]).then(() => { - this.doRemove(match, false, true); + this.doRemove(match); }); } From 60f27207df1668f7c95d12d1abe4f051c8bba990 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 10 Jan 2020 13:57:35 +0100 Subject: [PATCH 119/843] #88293 remove matches after replace --- src/vs/workbench/contrib/search/common/searchModel.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index 7e0db5d1da4..b0c40aa7ca6 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -549,9 +549,7 @@ export class FolderMatch extends Disposable { replaceAll(): Promise { const matches = this.matches(); - return this.replaceService.replace(matches).then(() => { - matches.forEach(match => this.doRemove(match, false, true)); - }); + return this.replaceService.replace(matches).then(() => this.doRemove(matches)); } matches(): FileMatch[] { From f82e3c0000250a9db08cc5db10a7ef859ba8eb4d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 15:13:59 +0100 Subject: [PATCH 120/843] don't apply code action when things have changed in the meantime --- src/vs/base/common/map.ts | 4 +- .../contrib/bulkEdit/browser/bulkEditPane.ts | 29 ++++- .../bulkEdit/browser/bulkEditPreview.ts | 12 +- .../bulkEdit/browser/bulkEditService.ts | 19 +-- .../services/bulkEdit/browser/conflicts.ts | 118 ++++++++++++++++++ 5 files changed, 156 insertions(+), 26 deletions(-) create mode 100644 src/vs/workbench/services/bulkEdit/browser/conflicts.ts diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 61d2436b045..4f6b55c3fe5 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -454,8 +454,8 @@ export class ResourceMap { return this.map.delete(this.toKey(resource)); } - forEach(clb: (value: T) => void): void { - this.map.forEach(clb); + forEach(clb: (value: T, key: URI) => void): void { + this.map.forEach((value, index) => clb(value, URI.parse(index))); } values(): T[] { diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 510aa68d0ac..c042d0a3e7c 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -26,6 +26,9 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import Severity from 'vs/base/common/severity'; +import { isFalsyOrEmpty } from 'vs/base/common/arrays'; const enum State { Data = 'data', @@ -54,6 +57,7 @@ export class BulkEditPane extends ViewPane { @IEditorService private readonly _editorService: IEditorService, @ILabelService private readonly _labelService: ILabelService, @ITextModelService private readonly _textModelService: ITextModelService, + @IDialogService private readonly _dialogService: IDialogService, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, @@ -140,8 +144,9 @@ export class BulkEditPane extends ViewPane { const input = await this._instaService.invokeFunction(BulkFileOperations.create, edit); const provider = this._instaService.createInstance(BulkEditPreviewProvider, input); - this._sessionDisposables.add(provider); + this._sessionDisposables.add(input); + this._currentInput = input; this._acceptAction.enabled = true; @@ -167,7 +172,21 @@ export class BulkEditPane extends ViewPane { } accept(): void { - this._done(true); + + const conflicts = this._currentInput?.conflicts.list(); + + if (isFalsyOrEmpty(conflicts)) { + this._done(true); + } + + let message: string; + if (conflicts!.length === 1) { + message = localize('conflict.1', "Cannot apply refactoring because '{0}' has changed in the meantime.", this._labelService.getUriLabel(conflicts![0], { relative: true })); + } else { + message = localize('conflict.N', "Cannot apply refactoring because {0} other files have changed in the meantime.", conflicts!.length); + } + + this._dialogService.show(Severity.Warning, message, []).finally(() => this._done(false)); } discard() { @@ -182,19 +201,19 @@ export class BulkEditPane extends ViewPane { } private _done(accept: boolean): void { - this._setState(State.Message); - this._sessionDisposables.clear(); if (this._currentResolve) { this._currentResolve(accept ? this._currentInput?.asWorkspaceEdit() : undefined); this._acceptAction.enabled = false; this._discardAction.enabled = false; + this._currentInput = undefined; } + this._setState(State.Message); + this._sessionDisposables.clear(); } private async _previewTextEditElement(element: TextEditElement): Promise { let leftResource: URI; - try { (await this._textModelService.createModelReference(element.parent.uri)).dispose(); leftResource = element.parent.uri; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index 4f93c0b9ad1..0414f0d1a27 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -17,6 +17,7 @@ import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiati import { IFileService } from 'vs/platform/files/common/files'; import { Emitter, Event } from 'vs/base/common/event'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { ConflictDetector } from 'vs/workbench/services/bulkEdit/browser/conflicts'; class CheckedObject { @@ -88,10 +89,19 @@ export class BulkFileOperations { readonly fileOperations: BulkFileOperation[] = []; + readonly conflicts: ConflictDetector; + constructor( private readonly _bulkEdit: WorkspaceEdit, @IFileService private readonly _fileService: IFileService, - ) { } + @IInstantiationService instaService: IInstantiationService, + ) { + this.conflicts = instaService.createInstance(ConflictDetector, _bulkEdit); + } + + dispose(): void { + this.conflicts.dispose(); + } async _init() { const operationByResource = new Map(); diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 97140519b5d..11136917170 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -25,25 +25,8 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { Recording } from 'vs/workbench/services/bulkEdit/browser/conflicts'; -abstract class Recording { - - static start(fileService: IFileService): Recording { - - let _changes = new Set(); - let subscription = fileService.onAfterOperation(e => { - _changes.add(e.resource.toString()); - }); - - return { - stop() { return subscription.dispose(); }, - hasChanged(resource) { return _changes.has(resource.toString()); } - }; - } - - abstract stop(): void; - abstract hasChanged(resource: URI): boolean; -} type ValidationResult = { canApply: true } | { canApply: false, reason: URI }; diff --git a/src/vs/workbench/services/bulkEdit/browser/conflicts.ts b/src/vs/workbench/services/bulkEdit/browser/conflicts.ts new file mode 100644 index 00000000000..6c9c2dc6c0a --- /dev/null +++ b/src/vs/workbench/services/bulkEdit/browser/conflicts.ts @@ -0,0 +1,118 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IFileService } from 'vs/platform/files/common/files'; +import { URI } from 'vs/base/common/uri'; +import { WorkspaceEdit, isResourceTextEdit } from 'vs/editor/common/modes'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ResourceMap } from 'vs/base/common/map'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; +import type { ITextModel } from 'vs/editor/common/model'; + +export abstract class Recording { + + static start(fileService: IFileService): Recording { + + let _changes = new Set(); + let subscription = fileService.onAfterOperation(e => { + _changes.add(e.resource.toString()); + }); + + return { + stop() { return subscription.dispose(); }, + hasChanged(resource) { return _changes.has(resource.toString()); } + }; + } + + abstract stop(): void; + abstract hasChanged(resource: URI): boolean; +} + +export class ConflictDetector { + + private readonly _conflicts = new ResourceMap(); + private readonly _changes = new ResourceMap(); + private readonly _disposables = new DisposableStore(); + + private readonly _onDidConflict = new Emitter(); + readonly onDidConflict: Event = this._onDidConflict.event; + + constructor( + workspaceEdit: WorkspaceEdit, + @IFileService fileService: IFileService, + @IModelService modelService: IModelService, + ) { + + const _workspaceEditResources = new ResourceMap(); + + for (let edit of workspaceEdit.edits) { + if (isResourceTextEdit(edit)) { + + _workspaceEditResources.set(edit.resource, true); + + if (typeof edit.modelVersionId === 'number') { + const model = modelService.getModel(edit.resource); + if (model && model.getVersionId() !== edit.modelVersionId) { + this._conflicts.set(edit.resource, true); + this._onDidConflict.fire(this); + } + } + + } else if (edit.newUri) { + _workspaceEditResources.set(edit.newUri, true); + + } else if (edit.oldUri) { + _workspaceEditResources.set(edit.oldUri, true); + } + } + + // listen to file changes + this._disposables.add(fileService.onFileChanges(e => { + for (let change of e.changes) { + + // change + this._changes.set(change.resource, true); + + // conflict + if (_workspaceEditResources.has(change.resource)) { + this._conflicts.set(change.resource, true); + this._onDidConflict.fire(this); + } + } + })); + + + // listen to model changes...? + const onDidChangeModel = (model: ITextModel) => { + // change + this._changes.set(model.uri, true); + + // conflict + if (_workspaceEditResources.has(model.uri)) { + this._conflicts.set(model.uri, true); + this._onDidConflict.fire(this); + } + }; + for (let model of modelService.getModels()) { + this._disposables.add(model.onDidChangeContent(() => onDidChangeModel(model))); + } + } + + dispose(): void { + this._disposables.dispose(); + this._onDidConflict.dispose(); + } + + list(): URI[] { + const result: URI[] = this._conflicts.keys(); + this._changes.forEach((_value, key) => { + if (!this._conflicts.has(key)) { + result.push(key); + } + }); + return result; + } +} From 3db53cb50daee101a0b3c7ece795ee415fd25ede Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 15:14:53 +0100 Subject: [PATCH 121/843] use clear icon for discarding changes --- src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index c042d0a3e7c..60c9879b907 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -42,8 +42,8 @@ export class BulkEditPane extends ViewPane { private _tree!: WorkbenchAsyncDataTree; private _message!: HTMLSpanElement; - private readonly _acceptAction = new Action('ok', localize('ok', "Apply Refactoring"), 'codicon-check', false, async () => this.accept()); - private readonly _discardAction = new Action('discard', localize('discard', "Discard"), 'codicon-trash', false, async () => this.discard()); + private readonly _acceptAction = new Action('ok', localize('ok', "Apply Changes"), 'codicon-check', false, async () => this.accept()); + private readonly _discardAction = new Action('discard', localize('discard', "Discard Changes"), 'codicon-clear-all', false, async () => this.discard()); private readonly _disposables = new DisposableStore(); From db581614ab57d2a1eb8925c56b52f60bddcace1c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 9 Jan 2020 15:25:57 +0100 Subject: [PATCH 122/843] Error while computing semantic tokens. Fixes #88366 --- .../server/src/modes/javascriptSemanticTokens.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts index fa615319354..7e4deafa262 100644 --- a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts +++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts @@ -20,7 +20,7 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current if (node.kind === ts.SyntaxKind.Identifier) { const symbol = typeChecker.getSymbolAtLocation(node); if (symbol) { - const decl = symbol.valueDeclaration || symbol.declarations[0]; + const decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; if (decl) { let typeIdx = tokenFromDeclarationMapping[decl.kind]; let modifierSet = 0; From 94ae236fe396ab819292cc00ceff4f3766d6c008 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 10 Jan 2020 15:19:54 +0100 Subject: [PATCH 123/843] Theme is not enabled after sync. Fixes #88364 --- .../themes/browser/workbenchThemeService.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index a914477d227..a3b675c68cc 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -192,6 +192,15 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration, tokenColorCustomizationConfiguration); + let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); + if (colorThemeSetting !== this.currentColorTheme.settingsId) { + const theme = await this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined); + if (theme) { + this.setColorTheme(theme.id, undefined); + return; + } + } + if (this.currentColorTheme.isLoaded) { const themeData = await this.colorThemeStore.findThemeData(this.currentColorTheme.id); if (!themeData) { @@ -216,6 +225,15 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { iconThemeSettingSchema.enumDescriptions = [iconThemeSettingSchema.enumDescriptions![0], ...event.themes.map(t => t.description || '')]; configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration); + let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); + if (iconThemeSetting !== this.currentIconTheme.settingsId) { + const theme = await this.iconThemeStore.findThemeBySettingsId(iconThemeSetting); + if (theme) { + this.setFileIconTheme(theme.id, undefined); + return; + } + } + if (this.currentIconTheme.isLoaded) { const theme = await this.iconThemeStore.findThemeData(this.currentIconTheme.id); if (!theme) { From 5a23709e39ab584755c648fa049f5a94e6ee5fb9 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Fri, 10 Jan 2020 15:34:17 +0100 Subject: [PATCH 124/843] WIP for #39441 --- .../client/src/cssMain.ts | 15 ++++++++- src/vs/editor/common/config/editorOptions.ts | 13 +++++++- .../editor/contrib/suggest/media/suggest.css | 31 ++++++++++++++++--- src/vs/editor/contrib/suggest/suggest.ts | 2 +- .../contrib/suggest/suggestController.ts | 2 +- .../editor/contrib/suggest/suggestWidget.ts | 9 +++++- .../suggest/test/completionModel.test.ts | 1 + src/vs/monaco.d.ts | 4 +++ 8 files changed, 68 insertions(+), 9 deletions(-) diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index 936176e9103..bc8bbdc9ff7 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -45,8 +45,8 @@ export function activate(context: ExtensionContext) { dataPaths }, middleware: { - // testing the replace / insert mode provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult { + // testing the replace / insert mode function updateRanges(item: CompletionItem) { const range = item.range; if (range && range.end.isAfter(position) && range.start.isBeforeOrEqual(position)) { @@ -54,10 +54,23 @@ export function activate(context: ExtensionContext) { item.range = undefined; } } + function updateProposals(r: CompletionItem[] | CompletionList | null | undefined): CompletionItem[] | CompletionList | null | undefined { if (r) { (Array.isArray(r) ? r : r.items).forEach(updateRanges); + + if (!Array.isArray(r)) { + r.isDetailsResolved = true; + r.items.forEach(i => { + if (i.kind === CompletionItemKind.Color) { + i.detail = i.documentation?.toString(); + } else { + i.detail = i.label; + } + }); + } } + return r; } const isThenable = (obj: ProviderResult): obj is Thenable => obj && (obj)['then']; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 94af6b10d1b..feb53271431 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2495,6 +2495,10 @@ export interface ISuggestOptions { * Max suggestions to show in suggestions. Defaults to 12. */ maxVisibleSuggestions?: number; + /** + * Always show inline details + */ + alwaysRevealInlineDetails?: boolean; /** * Show method-suggestions. */ @@ -2614,6 +2618,7 @@ class EditorSuggest extends BaseEditorOption .contents > .main > .readMore, +/** Inline type Label (details) **/ + .monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label, +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .type-label { + display: none; +} + +.monaco-editor .suggest-widget.always-reveal-inline-details .monaco-list .monaco-list-row > .contents > .main > .type-label, +.monaco-editor .suggest-widget.always-reveal-inline-details.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .type-label { + display: inline; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .type-label { + display: inline; +} + +/** readMore icon **/ + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore, .monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .type-label, .monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { display: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .type-label { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { + display: inline; +} +.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.focused) > .contents > .main > .readMore { + display: inline; + visibility: hidden; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { display: inline; } diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index c2d3185ce7d..f408f88ec12 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -47,7 +47,7 @@ export class CompletionItem { idx?: number; word?: string; - // + // all details resolved, we can show them all readonly isDetailsResolved: boolean; constructor( diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index cdf2e4f41ef..a4330d1d438 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -43,7 +43,7 @@ import { SuggestRangeHighlighter } from 'vs/editor/contrib/suggest/suggestRangeH * Stop suggest widget from disappearing when clicking into other areas * For development purpose only */ -const _sticky = false; +const _sticky = true; class LineSuffix { diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 85358ea708f..24bb13a3043 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -517,7 +517,9 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate toggleClass(this.element, 'no-icons', !this.editor.getOption(EditorOption.suggest).showIcons); + const applyAlwaysRevealInlineDetailsStyle = () => toggleClass(this.element, 'always-reveal-inline-details', this.editor.getOption(EditorOption.suggest).alwaysRevealInlineDetails); applyIconStyle(); + applyAlwaysRevealInlineDetailsStyle(); let renderer = instantiationService.createInstance(Renderer, this, this.editor, triggerKeybindingLabel); @@ -538,7 +540,12 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate this.onListSelection(e))); this.toDispose.add(this.list.onFocusChange(e => this.onListFocus(e))); this.toDispose.add(this.editor.onDidChangeCursorSelection(() => this.onCursorSelectionChanged())); - this.toDispose.add(this.editor.onDidChangeConfiguration(e => e.hasChanged(EditorOption.suggest) && applyIconStyle())); + this.toDispose.add(this.editor.onDidChangeConfiguration(e => { + if (e.hasChanged(EditorOption.suggest)) { + applyIconStyle(); + applyAlwaysRevealInlineDetailsStyle(); + } + })); this.suggestWidgetVisible = SuggestContext.Visible.bindTo(contextKeyService); this.suggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(contextKeyService); diff --git a/src/vs/editor/contrib/suggest/test/completionModel.test.ts b/src/vs/editor/contrib/suggest/test/completionModel.test.ts index 6a26c5fd584..c3bd3d76100 100644 --- a/src/vs/editor/contrib/suggest/test/completionModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/completionModel.test.ts @@ -41,6 +41,7 @@ suite('CompletionModel', function () { shareSuggestSelections: false, showIcons: true, maxVisibleSuggestions: 12, + alwaysRevealInlineDetails: false, showMethods: true, showFunctions: true, showConstructors: true, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index ace85852ace..ba478dbc33a 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3381,6 +3381,10 @@ declare namespace monaco.editor { * Max suggestions to show in suggestions. Defaults to 12. */ maxVisibleSuggestions?: number; + /** + * Always show inline details + */ + alwaysRevealInlineDetails?: boolean; /** * Show method-suggestions. */ From cf0fd9a5e7aa19375dd7cc5b9606e1b7400ef91a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 10 Jan 2020 15:34:37 +0100 Subject: [PATCH 125/843] editors - save dirty working copy after delay (for #84672) --- .../browser/parts/editor/editorAutoSave.ts | 82 +++++++++---- .../files/test/browser/editorAutoSave.test.ts | 113 ++++++++++++++++++ .../textfile/common/textFileEditorModel.ts | 66 +--------- .../workingCopy/common/workingCopyService.ts | 14 ++- .../test/common/workingCopyService.test.ts | 5 + 5 files changed, 195 insertions(+), 85 deletions(-) create mode 100644 src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts diff --git a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts index cb0c402f835..c36cd20e41d 100644 --- a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts +++ b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts @@ -4,16 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { Disposable, DisposableStore, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { IFilesConfigurationService, AutoSaveMode, IAutoSaveConfiguration } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { SaveReason, IEditorIdentifier, IEditorInput, GroupIdentifier } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; export class EditorAutoSave extends Disposable implements IWorkbenchContribution { + // Auto save: after delay + private autoSaveAfterDelay: number | undefined; + private readonly pendingAutoSavesAfterDelay = new Map(); + + // Auto save: focus change & window change private lastActiveEditor: IEditorInput | undefined = undefined; private lastActiveGroupId: GroupIdentifier | undefined = undefined; private lastActiveEditorControlDisposable = this._register(new DisposableStore()); @@ -22,17 +28,22 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, @IHostService private readonly hostService: IHostService, @IEditorService private readonly editorService: IEditorService, - @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, + @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService ) { super(); + // Figure out initial auto save config + this.onAutoSaveConfigurationChange(filesConfigurationService.getAutoSaveConfiguration(), false); + this.registerListeners(); } private registerListeners(): void { this._register(this.hostService.onDidChangeFocus(focused => this.onWindowFocusChange(focused))); this._register(this.editorService.onDidActiveEditorChange(() => this.onDidActiveEditorChange())); - this._register(this.filesConfigurationService.onAutoSaveConfigurationChange(() => this.onAutoSaveConfigurationChange())); + this._register(this.filesConfigurationService.onAutoSaveConfigurationChange(config => this.onAutoSaveConfigurationChange(config, true))); + this._register(this.workingCopyService.onDidChangeDirty(workingCopy => this.onDidWorkingCopyChangeDirty(workingCopy))); } private onWindowFocusChange(focused: boolean): void { @@ -85,24 +96,55 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution } } - private onAutoSaveConfigurationChange(): void { - let reason: SaveReason | undefined = undefined; - switch (this.filesConfigurationService.getAutoSaveMode()) { - case AutoSaveMode.ON_FOCUS_CHANGE: - reason = SaveReason.FOCUS_CHANGE; - break; - case AutoSaveMode.ON_WINDOW_CHANGE: - reason = SaveReason.WINDOW_CHANGE; - break; - case AutoSaveMode.AFTER_SHORT_DELAY: - case AutoSaveMode.AFTER_LONG_DELAY: - reason = SaveReason.AUTO; - break; - } + private onAutoSaveConfigurationChange(config: IAutoSaveConfiguration, fromEvent: boolean): void { + + // Update auto save after delay config + this.autoSaveAfterDelay = (typeof config.autoSaveDelay === 'number') && config.autoSaveDelay > 0 ? config.autoSaveDelay : undefined; // Trigger a save-all when auto save is enabled - if (reason) { - this.editorService.saveAll({ reason }); + if (fromEvent) { + let reason: SaveReason | undefined = undefined; + switch (this.filesConfigurationService.getAutoSaveMode()) { + case AutoSaveMode.ON_FOCUS_CHANGE: + reason = SaveReason.FOCUS_CHANGE; + break; + case AutoSaveMode.ON_WINDOW_CHANGE: + reason = SaveReason.WINDOW_CHANGE; + break; + case AutoSaveMode.AFTER_SHORT_DELAY: + case AutoSaveMode.AFTER_LONG_DELAY: + reason = SaveReason.AUTO; + break; + } + + if (reason) { + this.editorService.saveAll({ reason }); + } + } + } + + private onDidWorkingCopyChangeDirty(workingCopy: IWorkingCopy): void { + if (typeof this.autoSaveAfterDelay !== 'number') { + return; // auto save after delay must be enabled + } + + if (workingCopy.capabilities & WorkingCopyCapabilities.Untitled) { + return; // we never auto save untitled working copies + } + + // Clear any running auto save operation + dispose(this.pendingAutoSavesAfterDelay.get(workingCopy)); + this.pendingAutoSavesAfterDelay.delete(workingCopy); + + // Working copy got dirty - start auto save + if (workingCopy.isDirty()) { + const handle = setTimeout(() => { + if (workingCopy.isDirty()) { + workingCopy.save({ reason: SaveReason.AUTO }); + } + }, this.autoSaveAfterDelay); + + this.pendingAutoSavesAfterDelay.set(workingCopy, toDisposable(() => clearTimeout(handle))); } } } diff --git a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts new file mode 100644 index 00000000000..68ba4c0060d --- /dev/null +++ b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts @@ -0,0 +1,113 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { Event } from 'vs/base/common/event'; +import { toResource } from 'vs/base/test/common/utils'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { workbenchInstantiationService, TestTextFileService, TestFileService, TestFilesConfigurationService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices'; +import { ITextFileService, IResolvedTextFileEditorModel, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { TextFileEditor } from 'vs/workbench/contrib/files/browser/editors/textFileEditor'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; +import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; +import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; +import { EditorAutoSave } from 'vs/workbench/browser/parts/editor/editorAutoSave'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; + +class ServiceAccessor { + constructor( + @IEditorService public editorService: IEditorService, + @IEditorGroupsService public editorGroupService: IEditorGroupsService, + @ITextFileService public textFileService: TestTextFileService, + @IFileService public fileService: TestFileService, + @IUntitledTextEditorService public untitledTextEditorService: IUntitledTextEditorService, + @IConfigurationService public configurationService: TestConfigurationService + ) { + } +} + +suite('EditorAutoSave', () => { + + let disposables: IDisposable[] = []; + + setup(() => { + disposables.push(Registry.as(EditorExtensions.Editors).registerEditor( + EditorDescriptor.create( + TextFileEditor, + TextFileEditor.ID, + 'Text File Editor' + ), + [new SyncDescriptor(FileEditorInput)] + )); + }); + + teardown(() => { + dispose(disposables); + disposables = []; + }); + + test('editor auto saves after short delay if configured', async function () { + const instantiationService = workbenchInstantiationService(); + + const configurationService = new TestConfigurationService(); + configurationService.setUserConfiguration('files', { autoSave: 'afterDelay', autoSaveDelay: 1 }); + instantiationService.stub(IConfigurationService, configurationService); + + instantiationService.stub(IFilesConfigurationService, new TestFilesConfigurationService( + instantiationService.createInstance(MockContextKeyService), + configurationService, + TestEnvironmentService + )); + + const part = instantiationService.createInstance(EditorPart); + part.create(document.createElement('div')); + part.layout(400, 300); + + instantiationService.stub(IEditorGroupsService, part); + + const editorService: EditorService = instantiationService.createInstance(EditorService); + instantiationService.stub(IEditorService, editorService); + + const accessor = instantiationService.createInstance(ServiceAccessor); + + const editorAutoSave = instantiationService.createInstance(EditorAutoSave); + + const resource = toResource.call(this, '/path/index.txt'); + + const model = await accessor.textFileService.models.loadOrCreate(resource) as IResolvedTextFileEditorModel; + + model.textEditorModel.setValue('Super Good'); + + assert.ok(model.isDirty()); + + await awaitModelSaved(model); + + assert.ok(!model.isDirty()); + + part.dispose(); + editorAutoSave.dispose(); + (accessor.textFileService.models).clear(); + (accessor.textFileService.models).dispose(); + }); + + function awaitModelSaved(model: ITextFileEditorModel): Promise { + return new Promise(c => { + Event.once(model.onDidChangeDirty)(c); + }); + } +}); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index a8458d9f09f..28149c01025 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -24,13 +24,12 @@ import { RunOnceScheduler, timeout } from 'vs/base/common/async'; import { ITextBufferFactory } from 'vs/editor/common/model'; import { hash } from 'vs/base/common/hash'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { isEqual, isEqualOrParent, extname, basename, joinPath } from 'vs/base/common/resources'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Schemas } from 'vs/base/common/network'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -import { IFilesConfigurationService, IAutoSaveConfiguration } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; export interface IBackupMetaData { mtime: number; @@ -92,10 +91,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private lastResolvedFileStat: IFileStatWithMetadata | undefined; - private autoSaveAfterMillies: number | undefined; - private autoSaveAfterMilliesEnabled: boolean | undefined; - private readonly autoSaveDisposable = this._register(new MutableDisposable()); - private readonly saveSequentializer = new SaveSequentializer(); private lastSaveAttemptTime = 0; @@ -128,8 +123,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil ) { super(modelService, modeService); - this.updateAutoSaveConfiguration(filesConfigurationService.getAutoSaveConfiguration()); - // Make known to working copy service this._register(this.workingCopyService.registerWorkingCopy(this)); @@ -138,7 +131,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private registerListeners(): void { this._register(this.fileService.onFileChanges(e => this.onFileChanges(e))); - this._register(this.filesConfigurationService.onAutoSaveConfigurationChange(config => this.updateAutoSaveConfiguration(config))); this._register(this.filesConfigurationService.onFilesAssociationChange(e => this.onFilesAssociationChange())); this._register(this.onDidStateChange(e => this.onStateChange(e))); } @@ -206,13 +198,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - private updateAutoSaveConfiguration(config: IAutoSaveConfiguration): void { - const autoSaveAfterMilliesEnabled = (typeof config.autoSaveDelay === 'number') && config.autoSaveDelay > 0; - - this.autoSaveAfterMilliesEnabled = autoSaveAfterMilliesEnabled; - this.autoSaveAfterMillies = autoSaveAfterMilliesEnabled ? config.autoSaveDelay : undefined; - } - private onFilesAssociationChange(): void { if (!this.isResolved()) { return; @@ -258,9 +243,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return false; } - // Cancel any running auto-save - this.autoSaveDisposable.clear(); - // Unset flags const wasDirty = this.dirty; const undo = this.setDirty(false); @@ -474,13 +456,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.createTextEditorModel(value, resource, this.preferredMode); // We restored a backup so we have to set the model as being dirty - // We also want to trigger auto save if it is enabled to simulate the exact same behaviour - // you would get if manually making the model dirty (fixes https://github.com/Microsoft/vscode/issues/16977) if (fromBackup) { this.doMakeDirty(); - if (this.autoSaveAfterMilliesEnabled) { - this.doAutoSave(this.versionId); - } } // Ensure we are not tracking a stale state @@ -536,9 +513,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // The contents changed as a matter of Undo and the version reached matches the saved one // In this case we clear the dirty flag and emit a SAVED event to indicate this state. - // Note: we currently only do this check when auto-save is turned off because there you see - // a dirty indicator that you want to get rid of when undoing to the saved version. - if (!this.autoSaveAfterMilliesEnabled && this.isResolved() && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) { + if (this.isResolved() && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) { this.logService.trace('onModelContentChanged() - model content changed back to last saved version', this.resource); // Clear flags @@ -559,15 +534,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Mark as dirty this.doMakeDirty(); - // Start auto save process unless we are in conflict resolution mode and unless it is disabled - if (this.autoSaveAfterMilliesEnabled) { - if (!this.inConflictMode) { - this.doAutoSave(this.versionId); - } else { - this.logService.trace('makeDirty() - prevented save because we are in conflict resolution mode', this.resource); - } - } - // Handle content change events this.contentChangeEventScheduler.schedule(); } @@ -593,27 +559,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - private doAutoSave(versionId: number): void { - this.logService.trace(`doAutoSave() - enter for versionId ${versionId}`, this.resource); - - // Cancel any currently running auto saves to make this the one that succeeds - this.autoSaveDisposable.clear(); - - // Create new save timer and store it for disposal as needed - const handle = setTimeout(() => { - - // Clear the timeout now that we are running - this.autoSaveDisposable.clear(); - - // Only trigger save if the version id has not changed meanwhile - if (versionId === this.versionId) { - this.doSave(versionId, { reason: SaveReason.AUTO }); // Very important here to not return the promise because if the timeout promise is canceled it will bubble up the error otherwise - do not change - } - }, this.autoSaveAfterMillies); - - this.autoSaveDisposable.value = toDisposable(() => clearTimeout(handle)); - } - async save(options: ITextFileSaveOptions = Object.create(null)): Promise { if (!this.isResolved()) { return false; @@ -621,9 +566,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.logService.trace('save() - enter', this.resource); - // Cancel any currently running auto saves to make this the one that succeeds - this.autoSaveDisposable.clear(); - await this.doSave(this.versionId, options); return true; @@ -676,8 +618,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Push all edit operations to the undo stack so that the user has a chance to - // Ctrl+Z back to the saved version. We only do this when auto-save is turned off - if (!this.autoSaveAfterMilliesEnabled && this.isResolved()) { + // Ctrl+Z back to the saved version. + if (this.isResolved()) { this.textEditorModel.pushStackElement(); } diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index 55d754bb392..d7a2b403a5f 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -9,19 +9,25 @@ import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { Disposable, IDisposable, toDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; +import { ISaveOptions } from 'vs/workbench/common/editor'; export const enum WorkingCopyCapabilities { /** * Signals that the working copy requires * additional input when saving, e.g. an - * associated path to save to. + * associated path to save to. */ Untitled = 1 << 1 } export interface IWorkingCopy { + readonly resource: URI; + + readonly capabilities: WorkingCopyCapabilities; + + //#region Dirty Tracking readonly onDidChangeDirty: Event; @@ -31,9 +37,11 @@ export interface IWorkingCopy { //#endregion - readonly resource: URI; + //#region Save - readonly capabilities: WorkingCopyCapabilities; + save(options?: ISaveOptions): Promise; + + //#endregion } export const IWorkingCopyService = createDecorator('workingCopyService'); diff --git a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts index a7797845a17..4bac643a90d 100644 --- a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts @@ -9,6 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { TestWorkingCopyService } from 'vs/workbench/test/workbenchTestServices'; +import type { ISaveOptions } from 'vs/workbench/common/editor'; suite('WorkingCopyService', () => { @@ -41,6 +42,10 @@ suite('WorkingCopyService', () => { return this.dirty; } + async save(options?: ISaveOptions): Promise { + return true; + } + dispose(): void { this._onDispose.fire(); From 4032ce7241853f261fa79b1f7c2acd6d689b6099 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 10 Jan 2020 15:37:37 +0100 Subject: [PATCH 126/843] polish --- .../src/features/semanticTokens.ts | 152 +++++++++--------- 1 file changed, 73 insertions(+), 79 deletions(-) diff --git a/extensions/typescript-language-features/src/features/semanticTokens.ts b/extensions/typescript-language-features/src/features/semanticTokens.ts index cc9a8ba582a..17623605687 100644 --- a/extensions/typescript-language-features/src/features/semanticTokens.ts +++ b/extensions/typescript-language-features/src/features/semanticTokens.ts @@ -7,35 +7,19 @@ import * as vscode from 'vscode'; import { ITypeScriptServiceClient, ExecConfig, ServerResponse } from '../typescriptService'; import * as Proto from '../protocol'; -enum TokenType { - 'class', - 'enum', - 'interface', - 'namespace', - 'typeParameter', - 'type', - 'parameter', - 'variable', - 'property', - 'constant', - 'function', - 'member', - _sentinel -} - - -enum TokenModifier { - 'declaration', - 'static', - 'async', - _sentinel +export function register(selector: vscode.DocumentSelector, client: ITypeScriptServiceClient) { + const provider = new SemanticTokensProvider(client); + return vscode.languages.registerSemanticTokensProvider(selector, provider, provider.getLegend()); + } +/* + * Prototype of a SemanticTokensProvider, relying on the experimental `encodedSemanticClassifications-full` request from the TypeScript server. + * As the results retured by the TypeScript server are limited, we also add a Typescript plugin (typescript-vscode-sh-plugin) to enrich the returned token. + */ class SemanticTokensProvider implements vscode.SemanticTokensProvider { - constructor( - private readonly client: ITypeScriptServiceClient - ) { + constructor(private readonly client: ITypeScriptServiceClient) { } getLegend(): vscode.SemanticTokensLegend { @@ -58,79 +42,91 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider { const versionBeforeRequest = document.version; - if (_options.ranges) { + const allTokenSpans: number[][] = []; - // const allArgs = _options.ranges.map(r => ({file, start: document.offsetAt(r.start), length: document.offsetAt(r.end) - document.offsetAt(r.start)})); + let requestArgs: ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs[] = []; + if (_options.ranges) { + requestArgs = _options.ranges.map(r => { const start = document.offsetAt(r.start); const length = document.offsetAt(r.end) - start; return { file, start, length }; }); + requestArgs = requestArgs.sort((a1, a2) => a1.start - a2.start); + } else { + requestArgs = [{ file, start: 0, length: document.getText().length }]; // full file + } + for (const requestArg of requestArgs) { + const response = await (this.client as ExperimentalProtocol.IExtendedTypeScriptServiceClient).execute('encodedSemanticClassifications-full', requestArg, token); + if (response.type === 'response' && response.body) { + allTokenSpans.push(response.body.spans); + } else { + return null; + } } - const args: ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs = { - file: file, - start: 0, - length: document.getText().length, - }; - - const response = await (this.client as ExperimentalProtocol.IExtendedTypeScriptServiceClient).execute('encodedSemanticClassifications-full', args, token); const versionAfterRequest = document.version; - if (versionBeforeRequest !== versionAfterRequest) { // A new request will come in soon... return null; } - if (response.type !== 'response') { - return null; - } - if (!response.body) { - return null; - } - const builder = new vscode.SemanticTokensBuilder(); + for (const tokenSpan of allTokenSpans) { + for (let i = 0, len = Math.floor(tokenSpan.length / 3); i < len; i++) { - const tsTokens = response.body.spans; - for (let i = 0, len = Math.floor(tsTokens.length / 3); i < len; i++) { + const tsClassification = tokenSpan[3 * i + 2]; + let tokenType = 0; + let tokenModifiers = 0; + if (tsClassification >= 0x100) { + // exendend classifications as returned by the typescript-vscode-sh-plugin + tokenType = (tsClassification >> 8) - 1; + tokenModifiers = tsClassification & 0xFF; + } else { + tokenType = tokenTypeMap[tsClassification]; + if (tokenType === undefined) { + continue; + } + } - const tsClassification = tsTokens[3 * i + 2]; - let tokenType = 0; - let tokenModifiers = 0; - if (tsClassification > 0xFF) { - // classifications as returned by the typescript-vscode-sh-plugin - tokenType = (tsClassification >> 8) - 1; - tokenModifiers = tsClassification & 0xFF; - } else { - tokenType = tokenTypeMap[tsClassification]; - if (tokenType === undefined) { - continue; + const offset = tokenSpan[3 * i]; + const length = tokenSpan[3 * i + 1]; + + // we can use the document's range conversion methods because the result is at the same version as the document + const startPos = document.positionAt(offset); + const endPos = document.positionAt(offset + length); + + for (let line = startPos.line; line <= endPos.line; line++) { + const startCharacter = (line === startPos.line ? startPos.character : 0); + const endCharacter = (line === endPos.line ? endPos.character : document.lineAt(line).text.length); + builder.push(line, startCharacter, endCharacter - startCharacter, tokenType, tokenModifiers); } } - - const offset = tsTokens[3 * i]; - const length = tsTokens[3 * i + 1]; - - // we can use the document's range conversion methods because - // the result is at the same version as the document - const startPos = document.positionAt(offset); - const endPos = document.positionAt(offset + length); - - for (let line = startPos.line; line <= endPos.line; line++) { - const startCharacter = (line === startPos.line ? startPos.character : 0); - const endCharacter = (line === endPos.line ? endPos.character : document.lineAt(line).text.length); - builder.push(line, startCharacter, endCharacter - startCharacter, tokenType, tokenModifiers); - } } - return new vscode.SemanticTokens(builder.build()); } - } -export function register( - selector: vscode.DocumentSelector, - client: ITypeScriptServiceClient -) { - const provider = new SemanticTokensProvider(client); - return vscode.languages.registerSemanticTokensProvider(selector, provider, provider.getLegend()); +enum TokenType { + 'class', + 'enum', + 'interface', + 'namespace', + 'typeParameter', + 'type', + 'parameter', + 'variable', + 'property', + 'constant', + 'function', + 'member', + _sentinel } +enum TokenModifier { + 'declaration', + 'static', + 'async', + _sentinel +} + +// mapping for the original ExperimentalProtocol.ClassificationType from TypeScript (only used when plugin is not available) + const tokenTypeMap: number[] = []; tokenTypeMap[ExperimentalProtocol.ClassificationType.className] = TokenType.class; tokenTypeMap[ExperimentalProtocol.ClassificationType.enumName] = TokenType.enum; @@ -221,5 +217,3 @@ export namespace ExperimentalProtocol { 'encodedSemanticClassifications-full': [ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs, ExperimentalProtocol.EncodedSemanticClassificationsResponse]; } } - - From f1f652ccc191d40f06a84a4b468b9cf09f9406c6 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 10 Jan 2020 15:39:42 +0100 Subject: [PATCH 127/843] polish html-js sem highlighting --- .../src/modes/javascriptSemanticTokens.ts | 57 ++++++++++--------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts index 7e4deafa262..9a1f394977a 100644 --- a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts +++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts @@ -55,37 +55,42 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current return resultTokens; } +enum TokenType { + 'class', + 'enum', + 'interface', + 'namespace', + 'typeParameter', + 'type', + 'parameter', + 'variable', + 'property', + 'constant', + 'function', + 'member', + _sentinel +} + + +enum TokenModifier { + 'declaration', + 'static', + 'async', + _sentinel +} export function getSemanticTokenLegend() { + const tokenTypes = []; + for (let i = 0; i < TokenType._sentinel; i++) { + tokenTypes.push(TokenType[i]); + } + const tokenModifiers = []; + for (let i = 0; i < TokenModifier._sentinel; i++) { + tokenModifiers.push(TokenModifier[i]); + } return { types: tokenTypes, modifiers: tokenModifiers }; } - -const tokenTypes: string[] = ['class', 'enum', 'interface', 'namespace', 'typeParameter', 'type', 'parameter', 'variable', 'property', 'constant', 'function', 'member']; -const tokenModifiers: string[] = ['declaration', 'static', 'async']; - -const enum TokenType { - 'class' = 0, - 'enum' = 1, - 'interface' = 2, - 'namespace' = 3, - 'typeParameter' = 4, - 'type' = 5, - 'parameter' = 6, - 'variable' = 7, - 'property' = 8, - 'constant' = 9, - 'function' = 10, - 'member' = 11 -} - - -const enum TokenModifier { - 'declaration' = 0x01, - 'static' = 0x02, - 'async' = 0x04, -} - const tokenFromDeclarationMapping: { [name: string]: TokenType } = { [ts.SyntaxKind.VariableDeclaration]: TokenType.variable, [ts.SyntaxKind.Parameter]: TokenType.parameter, From 18ee991ed5961fc3c80c415ac7e6923323e381f2 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 15:45:31 +0100 Subject: [PATCH 128/843] fix issue with change check --- .../workbench/contrib/bulkEdit/browser/bulkEditPane.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 60c9879b907..ee46ddb38ef 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -28,7 +28,6 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; const enum State { Data = 'data', @@ -175,15 +174,16 @@ export class BulkEditPane extends ViewPane { const conflicts = this._currentInput?.conflicts.list(); - if (isFalsyOrEmpty(conflicts)) { + if (!conflicts || conflicts.length === 0) { this._done(true); + return; } let message: string; - if (conflicts!.length === 1) { - message = localize('conflict.1', "Cannot apply refactoring because '{0}' has changed in the meantime.", this._labelService.getUriLabel(conflicts![0], { relative: true })); + if (conflicts.length === 1) { + message = localize('conflict.1', "Cannot apply refactoring because '{0}' has changed in the meantime.", this._labelService.getUriLabel(conflicts[0], { relative: true })); } else { - message = localize('conflict.N', "Cannot apply refactoring because {0} other files have changed in the meantime.", conflicts!.length); + message = localize('conflict.N', "Cannot apply refactoring because {0} other files have changed in the meantime.", conflicts.length); } this._dialogService.show(Severity.Warning, message, []).finally(() => this._done(false)); From 0ab6605afd1a9a6ca87d2d9bfca349b3f9ef6a8f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 10 Jan 2020 15:47:08 +0100 Subject: [PATCH 129/843] text files - tweak logging --- .../browser/parts/editor/editorAutoSave.ts | 10 ++++- .../textfile/common/textFileEditorModel.ts | 40 +++++++++---------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts index c36cd20e41d..3a434d37c5d 100644 --- a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts +++ b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts @@ -12,6 +12,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { ILogService } from 'vs/platform/log/common/log'; export class EditorAutoSave extends Disposable implements IWorkbenchContribution { @@ -29,7 +30,8 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution @IHostService private readonly hostService: IHostService, @IEditorService private readonly editorService: IEditorService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, - @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService + @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, + @ILogService private readonly logService: ILogService ) { super(); @@ -88,6 +90,8 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution (reason === SaveReason.WINDOW_CHANGE && (mode === AutoSaveMode.ON_FOCUS_CHANGE || mode === AutoSaveMode.ON_WINDOW_CHANGE)) || (reason === SaveReason.FOCUS_CHANGE && mode === AutoSaveMode.ON_FOCUS_CHANGE) ) { + this.logService.trace(`[editor auto save] triggering auto save with reason ${reason}`); + if (editorIdentifier) { this.editorService.save(editorIdentifier, { reason }); } else { @@ -138,6 +142,8 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution // Working copy got dirty - start auto save if (workingCopy.isDirty()) { + this.logService.trace(`[editor auto save] starting auto save after ${this.autoSaveAfterDelay}ms`, workingCopy.resource.toString()); + const handle = setTimeout(() => { if (workingCopy.isDirty()) { workingCopy.save({ reason: SaveReason.AUTO }); @@ -145,6 +151,8 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution }, this.autoSaveAfterDelay); this.pendingAutoSavesAfterDelay.set(workingCopy, toDisposable(() => clearTimeout(handle))); + } else { + this.logService.trace(`[editor auto save] clearing auto save`, workingCopy.resource.toString()); } } } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 28149c01025..afd56f642e9 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -273,13 +273,13 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } async load(options?: ILoadOptions): Promise { - this.logService.trace('load() - enter', this.resource); + this.logService.trace('[text file model] load() - enter', this.resource.toString()); // It is very important to not reload the model when the model is dirty. // We also only want to reload the model from the disk if no save is pending // to avoid data loss. if (this.dirty || this.saveSequentializer.hasPendingSave()) { - this.logService.trace('load() - exit - without loading because model is dirty or being saved', this.resource); + this.logService.trace('[text file model] load() - exit - without loading because model is dirty or being saved', this.resource.toString()); return this; } @@ -296,7 +296,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil try { return await this.loadFromBackup(backup, options); } catch (error) { - this.logService.error(error); // ignore error and continue to load as file below + this.logService.error('[text file model] load()', error); // ignore error and continue to load as file below } } } @@ -396,7 +396,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private loadFromContent(content: ITextFileStreamContent, options?: ILoadOptions, fromBackup?: boolean): TextFileEditorModel { - this.logService.trace('load() - resolved content', this.resource); + this.logService.trace('[text file model] load() - resolved content', this.resource.toString()); // Update our resolved disk stat model this.updateLastResolvedFileStat({ @@ -450,7 +450,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private doCreateTextModel(resource: URI, value: ITextBufferFactory, fromBackup: boolean): void { - this.logService.trace('load() - created text editor model', this.resource); + this.logService.trace('[text file model] load() - created text editor model', this.resource.toString()); // Create model this.createTextEditorModel(value, resource, this.preferredMode); @@ -470,7 +470,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private doUpdateTextModel(value: ITextBufferFactory): void { - this.logService.trace('load() - updated text editor model', this.resource); + this.logService.trace('[text file model] load() - updated text editor model', this.resource.toString()); // Ensure we are not tracking a stale state this.setDirty(false); @@ -500,11 +500,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private onModelContentChanged(): void { - this.logService.trace(`onModelContentChanged() - enter`, this.resource); + this.logService.trace(`[text file model] onModelContentChanged() - enter`, this.resource.toString()); // In any case increment the version id because it tracks the textual content state of the model at all times this.versionId++; - this.logService.trace(`onModelContentChanged() - new versionId ${this.versionId}`, this.resource); + this.logService.trace(`[text file model] onModelContentChanged() - new versionId ${this.versionId}`, this.resource.toString()); // Ignore if blocking model changes if (this.blockModelContentChange) { @@ -514,7 +514,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // The contents changed as a matter of Undo and the version reached matches the saved one // In this case we clear the dirty flag and emit a SAVED event to indicate this state. if (this.isResolved() && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) { - this.logService.trace('onModelContentChanged() - model content changed back to last saved version', this.resource); + this.logService.trace('[text file model] onModelContentChanged() - model content changed back to last saved version', this.resource.toString()); // Clear flags const wasDirty = this.dirty; @@ -529,7 +529,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return; } - this.logService.trace('onModelContentChanged() - model content changed and marked as dirty', this.resource); + this.logService.trace('[text file model] onModelContentChanged() - model content changed and marked as dirty', this.resource.toString()); // Mark as dirty this.doMakeDirty(); @@ -564,7 +564,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return false; } - this.logService.trace('save() - enter', this.resource); + this.logService.trace('[text file model] save() - enter', this.resource.toString()); await this.doSave(this.versionId, options); @@ -576,7 +576,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil options.reason = SaveReason.EXPLICIT; } - this.logService.trace(`doSave(${versionId}) - enter with versionId ' + versionId`, this.resource); + this.logService.trace(`[text file model] doSave(${versionId}) - enter with versionId ' + versionId`, this.resource.toString()); // Lookup any running pending save for this versionId and return it if found // @@ -584,7 +584,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // while the save was not yet finished to disk // if (this.saveSequentializer.hasPendingSave(versionId)) { - this.logService.trace(`doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource); + this.logService.trace(`[text file model] doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource.toString()); return this.saveSequentializer.pendingSave || Promise.resolve(); } @@ -597,7 +597,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Thus we avoid spawning multiple auto saves and only take the latest. // if ((!options.force && !this.dirty) || versionId !== this.versionId) { - this.logService.trace(`doSave(${versionId}) - exit - because not dirty and/or versionId is different (this.isDirty: ${this.dirty}, this.versionId: ${this.versionId})`, this.resource); + this.logService.trace(`[text file model] doSave(${versionId}) - exit - because not dirty and/or versionId is different (this.isDirty: ${this.dirty}, this.versionId: ${this.versionId})`, this.resource.toString()); return Promise.resolve(); } @@ -611,7 +611,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // while the first save has not returned yet. // if (this.saveSequentializer.hasPendingSave()) { - this.logService.trace(`doSave(${versionId}) - exit - because busy saving`, this.resource); + this.logService.trace(`[text file model] doSave(${versionId}) - exit - because busy saving`, this.resource.toString()); // Register this as the next upcoming save and return return this.saveSequentializer.setNext(() => this.doSave(this.versionId /* make sure to use latest version id here */, options)); @@ -679,7 +679,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Save to Disk // mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering) - this.logService.trace(`doSave(${versionId}) - before write()`, this.resource); + this.logService.trace(`[text file model] doSave(${versionId}) - before write()`, this.resource.toString()); const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(lastResolvedFileStat.resource, this.createSnapshot(), { overwriteReadonly: options.overwriteReadonly, @@ -689,14 +689,14 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil etag: (options.ignoreModifiedSince || !this.filesConfigurationService.preventSaveConflicts(lastResolvedFileStat.resource)) ? ETAG_DISABLED : lastResolvedFileStat.etag, writeElevated: options.writeElevated }).then(stat => { - this.logService.trace(`doSave(${versionId}) - after write()`, this.resource); + this.logService.trace(`[text file model] doSave(${versionId}) - after write()`, this.resource.toString()); // Update dirty state unless model has changed meanwhile if (versionId === this.versionId) { - this.logService.trace(`doSave(${versionId}) - setting dirty to false because versionId did not change`, this.resource); + this.logService.trace(`[text file model] doSave(${versionId}) - setting dirty to false because versionId did not change`, this.resource.toString()); this.setDirty(false); } else { - this.logService.trace(`doSave(${versionId}) - not setting dirty to false because versionId did change meanwhile`, this.resource); + this.logService.trace(`[text file model] doSave(${versionId}) - not setting dirty to false because versionId did change meanwhile`, this.resource.toString()); } // Updated resolved stat with updated stat @@ -721,7 +721,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.telemetryService.publicLog2('filePUT', this.getTelemetryData(options.reason)); } }, error => { - this.logService.error(`doSave(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource); + this.logService.error(`[text file model] doSave(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource.toString()); // Flag as error state in the model this.inErrorMode = true; From 66e7c7a159b1f2dc13457b4af771c2bcc48dcb1d Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 10 Jan 2020 16:00:23 +0100 Subject: [PATCH 130/843] react to feedback --- src/vs/editor/browser/editorBrowser.ts | 5 +++-- src/vs/editor/browser/widget/codeEditorWidget.ts | 2 +- src/vs/editor/contrib/suggest/suggestWidget.ts | 4 ++-- src/vs/monaco.d.ts | 7 ------- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 4906fa6196a..cbbe9d17014 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -321,9 +321,10 @@ export interface IOverviewRuler { /** * Editor aria options. + * @internal */ export interface IEditorAriaOptions { - activeDescendent: string | undefined; + activeDescendant: string | undefined; } /** @@ -700,7 +701,7 @@ export interface ICodeEditor extends editorCommon.IEditor { * Sets the editor aria options, primarily the active descendent. * @internal */ - setAria(options: IEditorAriaOptions): void; + setAriaOptions(options: IEditorAriaOptions): void; /** * @internal diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index d0257074276..4409ad38d28 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1311,7 +1311,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._modelData.view.render(true, forceRedraw); } - public setAria(options: editorBrowser.IEditorAriaOptions): void { + public setAriaOptions(options: editorBrowser.IEditorAriaOptions): void { if (!this._modelData || !this._modelData.hasRealView) { return; } diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 8a0b22cdb52..f608af0238e 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -650,7 +650,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate Date: Fri, 10 Jan 2020 16:01:45 +0100 Subject: [PATCH 131/843] missed spelling --- src/vs/editor/browser/controller/textAreaHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index eff63581e43..604fee296f2 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -426,10 +426,10 @@ export class TextAreaHandler extends ViewPart { } public setAria(options: IEditorAriaOptions): void { - if (options.activeDescendent) { + if (options.activeDescendant) { this.textArea.setAttribute('aria-haspopup', 'true'); this.textArea.setAttribute('aria-autocomplete', 'list'); - this.textArea.setAttribute('aria-activedescendant', options.activeDescendent); + this.textArea.setAttribute('aria-activedescendant', options.activeDescendant); } else { this.textArea.setAttribute('aria-haspopup', 'false'); this.textArea.setAttribute('aria-autocomplete', 'both'); From 2470304e6d6aa81879a61ad5debb365ee68ce663 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 10 Jan 2020 16:03:51 +0100 Subject: [PATCH 132/843] set aria options --- src/vs/editor/browser/controller/textAreaHandler.ts | 2 +- src/vs/editor/browser/view/viewImpl.ts | 4 ++-- src/vs/editor/browser/widget/codeEditorWidget.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 604fee296f2..6ce7362aacc 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -425,7 +425,7 @@ export class TextAreaHandler extends ViewPart { return this._lastRenderPosition; } - public setAria(options: IEditorAriaOptions): void { + public setAriaOptions(options: IEditorAriaOptions): void { if (options.activeDescendant) { this.textArea.setAttribute('aria-haspopup', 'true'); this.textArea.setAttribute('aria-autocomplete', 'list'); diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 908dce7abb5..1af4947456b 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -510,8 +510,8 @@ export class View extends ViewEventHandler { this._textAreaHandler.refreshFocusState(); } - public setAria(options: IEditorAriaOptions): void { - this._textAreaHandler.setAria(options); + public setAriaOptions(options: IEditorAriaOptions): void { + this._textAreaHandler.setAriaOptions(options); } public addContentWidget(widgetData: IContentWidgetData): void { diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 4409ad38d28..9a9e71bf65f 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1315,7 +1315,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!this._modelData || !this._modelData.hasRealView) { return; } - this._modelData.view.setAria(options); + this._modelData.view.setAriaOptions(options); } public applyFontInfo(target: HTMLElement): void { From 703a2afcef92bcfb8596e136c56307675c9dedb0 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 10 Jan 2020 16:05:16 +0100 Subject: [PATCH 133/843] update typescript-vscode-sh-plugin --- extensions/typescript-language-features/package.json | 4 ++-- extensions/typescript-language-features/yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index df72fe3d01e..c63e6a90901 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -19,9 +19,9 @@ "jsonc-parser": "^2.1.1", "rimraf": "^2.6.3", "semver": "5.5.1", + "typescript-vscode-sh-plugin": "^0.3.0", "vscode-extension-telemetry": "0.1.1", - "vscode-nls": "^4.0.0", - "typescript-vscode-sh-plugin": "0.2.0" + "vscode-nls": "^4.0.0" }, "devDependencies": { "@types/node": "^12.11.7", diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index 6f2da650dd8..53bc5bf315d 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -626,10 +626,10 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -typescript-vscode-sh-plugin@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.2.0.tgz#8f1b79f2bc5a7d225235bf2b9fffdec9499f00f7" - integrity sha512-jp3B45VPwBuJ7I7IshkNuxn92/DUPqFq6Y3wiNE64Mmws0UCkXKbTsDT8+CKt5rFFCQL7PUtApj6aQkN8OKfxw== +typescript-vscode-sh-plugin@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.3.0.tgz#a071fa28187259a04e3d6862adba41a6332106ff" + integrity sha512-pqPgYa1L64/2LnhP/tJz/+hUsbKzQRMGCdp6Z5Z/dhAdjChB/0WZvovbwDlApGWxOvNAG+oub9TXnJ1yT0WfXQ== uri-js@^4.2.2: version "4.2.2" From 59e12621ae035508335fa7ab5d7935e16deda9e0 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 10 Jan 2020 16:15:08 +0100 Subject: [PATCH 134/843] polish --- .../src/features/semanticTokens.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/typescript-language-features/src/features/semanticTokens.ts b/extensions/typescript-language-features/src/features/semanticTokens.ts index 17623605687..fefb8e2271f 100644 --- a/extensions/typescript-language-features/src/features/semanticTokens.ts +++ b/extensions/typescript-language-features/src/features/semanticTokens.ts @@ -10,10 +10,9 @@ import * as Proto from '../protocol'; export function register(selector: vscode.DocumentSelector, client: ITypeScriptServiceClient) { const provider = new SemanticTokensProvider(client); return vscode.languages.registerSemanticTokensProvider(selector, provider, provider.getLegend()); - } -/* +/** * Prototype of a SemanticTokensProvider, relying on the experimental `encodedSemanticClassifications-full` request from the TypeScript server. * As the results retured by the TypeScript server are limited, we also add a Typescript plugin (typescript-vscode-sh-plugin) to enrich the returned token. */ @@ -49,7 +48,7 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider { requestArgs = _options.ranges.map(r => { const start = document.offsetAt(r.start); const length = document.offsetAt(r.end) - start; return { file, start, length }; }); requestArgs = requestArgs.sort((a1, a2) => a1.start - a2.start); } else { - requestArgs = [{ file, start: 0, length: document.getText().length }]; // full file + requestArgs = [{ file, start: 0, length: document.getText().length }]; // full document } for (const requestArg of requestArgs) { const response = await (this.client as ExperimentalProtocol.IExtendedTypeScriptServiceClient).execute('encodedSemanticClassifications-full', requestArg, token); @@ -102,6 +101,7 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider { } } +// Don't change TokenType and TokenModifier enums without adopting typescript-vscode-sh-plugin enum TokenType { 'class', 'enum', @@ -136,7 +136,7 @@ tokenTypeMap[ExperimentalProtocol.ClassificationType.typeParameterName] = TokenT tokenTypeMap[ExperimentalProtocol.ClassificationType.typeAliasName] = TokenType.type; tokenTypeMap[ExperimentalProtocol.ClassificationType.parameterName] = TokenType.parameter; -export namespace ExperimentalProtocol { +namespace ExperimentalProtocol { export interface IExtendedTypeScriptServiceClient { execute( From adb6281d0aea56c8e701714858c7fd593e030fc3 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 10 Jan 2020 16:38:32 +0100 Subject: [PATCH 135/843] fix html tests --- .../src/modes/javascriptSemanticTokens.ts | 57 +++++++++---------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts index 9a1f394977a..7e4deafa262 100644 --- a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts +++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts @@ -55,42 +55,37 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current return resultTokens; } -enum TokenType { - 'class', - 'enum', - 'interface', - 'namespace', - 'typeParameter', - 'type', - 'parameter', - 'variable', - 'property', - 'constant', - 'function', - 'member', - _sentinel -} - - -enum TokenModifier { - 'declaration', - 'static', - 'async', - _sentinel -} export function getSemanticTokenLegend() { - const tokenTypes = []; - for (let i = 0; i < TokenType._sentinel; i++) { - tokenTypes.push(TokenType[i]); - } - const tokenModifiers = []; - for (let i = 0; i < TokenModifier._sentinel; i++) { - tokenModifiers.push(TokenModifier[i]); - } return { types: tokenTypes, modifiers: tokenModifiers }; } + +const tokenTypes: string[] = ['class', 'enum', 'interface', 'namespace', 'typeParameter', 'type', 'parameter', 'variable', 'property', 'constant', 'function', 'member']; +const tokenModifiers: string[] = ['declaration', 'static', 'async']; + +const enum TokenType { + 'class' = 0, + 'enum' = 1, + 'interface' = 2, + 'namespace' = 3, + 'typeParameter' = 4, + 'type' = 5, + 'parameter' = 6, + 'variable' = 7, + 'property' = 8, + 'constant' = 9, + 'function' = 10, + 'member' = 11 +} + + +const enum TokenModifier { + 'declaration' = 0x01, + 'static' = 0x02, + 'async' = 0x04, +} + const tokenFromDeclarationMapping: { [name: string]: TokenType } = { [ts.SyntaxKind.VariableDeclaration]: TokenType.variable, [ts.SyntaxKind.Parameter]: TokenType.parameter, From caa505d53c86596cf06768e624da761e1f50b168 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 10 Jan 2020 16:39:19 +0100 Subject: [PATCH 136/843] debug editor contribution: move out logic to helper functions --- .../debug/browser/debugEditorContribution.ts | 259 +++++++++--------- 1 file changed, 130 insertions(+), 129 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 5f6e6681695..c22007db2f2 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -27,7 +27,7 @@ import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWid import { Position } from 'vs/editor/common/core/position'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; import { first } from 'vs/base/common/arrays'; -import { memoize } from 'vs/base/common/decorators'; +import { memoize, createMemoizer } from 'vs/base/common/decorators'; import { IEditorHoverOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { DebugHoverWidget } from 'vs/workbench/contrib/debug/browser/debugHover'; @@ -42,6 +42,125 @@ const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator when debugging. If exceeded ... is added const MAX_TOKENIZATION_LINE_LEN = 500; // If line is too long, then inline values for the line are skipped +function createInlineValueDecoration(lineNumber: number, contentText: string): IDecorationOptions { + // If decoratorText is too long, trim and add ellipses. This could happen for minified files with everything on a single line + if (contentText.length > MAX_INLINE_DECORATOR_LENGTH) { + contentText = contentText.substr(0, MAX_INLINE_DECORATOR_LENGTH) + '...'; + } + + return { + range: { + startLineNumber: lineNumber, + endLineNumber: lineNumber, + startColumn: Constants.MAX_SAFE_SMALL_INTEGER, + endColumn: Constants.MAX_SAFE_SMALL_INTEGER + }, + renderOptions: { + after: { + contentText, + backgroundColor: 'rgba(255, 200, 0, 0.2)', + margin: '10px' + }, + dark: { + after: { + color: 'rgba(255, 255, 255, 0.5)', + } + }, + light: { + after: { + color: 'rgba(0, 0, 0, 0.5)', + } + } + } + }; +} + +function createInlineValueDecorationsInsideRange(expressions: ReadonlyArray, range: Range, model: ITextModel, wordToLineNumbersMap: Map): IDecorationOptions[] { + const nameValueMap = new Map(); + for (let expr of expressions) { + nameValueMap.set(expr.name, expr.value); + // Limit the size of map. Too large can have a perf impact + if (nameValueMap.size >= MAX_NUM_INLINE_VALUES) { + break; + } + } + + const lineToNamesMap: Map = new Map(); + + // Compute unique set of names on each line + nameValueMap.forEach((_value, name) => { + const lineNumbers = wordToLineNumbersMap.get(name); + if (lineNumbers) { + for (let lineNumber of lineNumbers) { + if (range.containsPosition(new Position(lineNumber, 0))) { + if (!lineToNamesMap.has(lineNumber)) { + lineToNamesMap.set(lineNumber, []); + } + + if (lineToNamesMap.get(lineNumber)!.indexOf(name) === -1) { + lineToNamesMap.get(lineNumber)!.push(name); + } + } + } + } + }); + + const decorations: IDecorationOptions[] = []; + // Compute decorators for each line + lineToNamesMap.forEach((names, line) => { + const contentText = names.sort((first, second) => { + const content = model.getLineContent(line); + return content.indexOf(first) - content.indexOf(second); + }).map(name => `${name} = ${nameValueMap.get(name)}`).join(', '); + decorations.push(createInlineValueDecoration(line, contentText)); + }); + + return decorations; +} + +function getWordToLineNumbersMap(model: ITextModel | null): Map { + const result = new Map(); + if (!model) { + return result; + } + + // For every word in every line, map its ranges for fast lookup + for (let lineNumber = 1, len = model.getLineCount(); lineNumber <= len; ++lineNumber) { + const lineContent = model.getLineContent(lineNumber); + + // If line is too long then skip the line + if (lineContent.length > MAX_TOKENIZATION_LINE_LEN) { + continue; + } + + model.forceTokenization(lineNumber); + const lineTokens = model.getLineTokens(lineNumber); + for (let tokenIndex = 0, tokenCount = lineTokens.getCount(); tokenIndex < tokenCount; tokenIndex++) { + const tokenStartOffset = lineTokens.getStartOffset(tokenIndex); + const tokenEndOffset = lineTokens.getEndOffset(tokenIndex); + const tokenType = lineTokens.getStandardTokenType(tokenIndex); + const tokenStr = lineContent.substring(tokenStartOffset, tokenEndOffset); + + // Token is a word and not a comment + if (tokenType === StandardTokenType.Other) { + DEFAULT_WORD_REGEXP.lastIndex = 0; // We assume tokens will usually map 1:1 to words if they match + const wordMatch = DEFAULT_WORD_REGEXP.exec(tokenStr); + + if (wordMatch) { + const word = wordMatch[0]; + if (!result.has(word)) { + result.set(word, []); + } + + result.get(word)!.push(lineNumber); + } + } + } + } + + return result; +} + class DebugEditorContribution implements IDebugEditorContribution { private toDispose: IDisposable[]; @@ -49,8 +168,7 @@ class DebugEditorContribution implements IDebugEditorContribution { private nonDebugHoverPosition: Position | undefined; private hoverRange: Range | null = null; private mouseDown = false; - - private wordToLineNumbersMap: Map | undefined; + private static readonly MEMOIZER = createMemoizer(); private exceptionWidget: ExceptionWidget | undefined; @@ -95,7 +213,7 @@ class DebugEditorContribution implements IDebugEditorContribution { })); this.toDispose.push(this.editor.onKeyDown((e: IKeyboardEvent) => this.onKeyDown(e))); this.toDispose.push(this.editor.onDidChangeModelContent(() => { - this.wordToLineNumbersMap = undefined; + DebugEditorContribution.MEMOIZER.clear(); this.updateInlineValuesScheduler.schedule(); })); this.toDispose.push(this.editor.onDidChangeModel(async () => { @@ -107,7 +225,7 @@ class DebugEditorContribution implements IDebugEditorContribution { this.toggleExceptionWidget(); this.hideHoverWidget(); this.updateConfigurationWidgetVisibility(); - this.wordToLineNumbersMap = undefined; + DebugEditorContribution.MEMOIZER.clear(); await this.updateInlineValueDecorations(stackFrame); })); this.toDispose.push(this.editor.onDidScrollChange(() => this.hideHoverWidget)); @@ -118,6 +236,11 @@ class DebugEditorContribution implements IDebugEditorContribution { })); } + @DebugEditorContribution.MEMOIZER + private get wordToLineNumbersMap(): Map { + return getWordToLineNumbersMap(this.editor.getModel()); + } + private _applyHoverConfiguration(model: ITextModel, stackFrame: IStackFrame | undefined): void { if (stackFrame && model.uri.toString() === stackFrame.source.uri.toString()) { this.editor.updateOptions({ @@ -401,136 +524,14 @@ class DebugEditorContribution implements IDebugEditorContribution { range = range.setStartPosition(scope.range.startLineNumber, scope.range.startColumn); } - return this.createInlineValueDecorationsInsideRange(children, range, model); + return createInlineValueDecorationsInsideRange(children, range, model, this.wordToLineNumbersMap); })); + const allDecorations = decorationsPerScope.reduce((previous, current) => previous.concat(current), []); this.editor.setDecorations(INLINE_VALUE_DECORATION_KEY, allDecorations); } - private createInlineValueDecorationsInsideRange(expressions: ReadonlyArray, range: Range, model: ITextModel): IDecorationOptions[] { - const nameValueMap = new Map(); - for (let expr of expressions) { - nameValueMap.set(expr.name, expr.value); - // Limit the size of map. Too large can have a perf impact - if (nameValueMap.size >= MAX_NUM_INLINE_VALUES) { - break; - } - } - - const lineToNamesMap: Map = new Map(); - const wordToPositionsMap = this.getWordToPositionsMap(); - - // Compute unique set of names on each line - nameValueMap.forEach((value, name) => { - const positions = wordToPositionsMap.get(name); - if (positions) { - for (let position of positions) { - if (range.containsPosition(position)) { - if (!lineToNamesMap.has(position.lineNumber)) { - lineToNamesMap.set(position.lineNumber, []); - } - - if (lineToNamesMap.get(position.lineNumber)!.indexOf(name) === -1) { - lineToNamesMap.get(position.lineNumber)!.push(name); - } - } - } - } - }); - - const decorations: IDecorationOptions[] = []; - // Compute decorators for each line - lineToNamesMap.forEach((names, line) => { - const contentText = names.sort((first, second) => { - const content = model.getLineContent(line); - return content.indexOf(first) - content.indexOf(second); - }).map(name => `${name} = ${nameValueMap.get(name)}`).join(', '); - decorations.push(this.createInlineValueDecoration(line, contentText)); - }); - - return decorations; - } - - private createInlineValueDecoration(lineNumber: number, contentText: string): IDecorationOptions { - // If decoratorText is too long, trim and add ellipses. This could happen for minified files with everything on a single line - if (contentText.length > MAX_INLINE_DECORATOR_LENGTH) { - contentText = contentText.substr(0, MAX_INLINE_DECORATOR_LENGTH) + '...'; - } - - return { - range: { - startLineNumber: lineNumber, - endLineNumber: lineNumber, - startColumn: Constants.MAX_SAFE_SMALL_INTEGER, - endColumn: Constants.MAX_SAFE_SMALL_INTEGER - }, - renderOptions: { - after: { - contentText, - backgroundColor: 'rgba(255, 200, 0, 0.2)', - margin: '10px' - }, - dark: { - after: { - color: 'rgba(255, 255, 255, 0.5)', - } - }, - light: { - after: { - color: 'rgba(0, 0, 0, 0.5)', - } - } - } - }; - } - - private getWordToPositionsMap(): Map { - if (!this.wordToLineNumbersMap) { - this.wordToLineNumbersMap = new Map(); - const model = this.editor.getModel(); - if (!model) { - return this.wordToLineNumbersMap; - } - - // For every word in every line, map its ranges for fast lookup - for (let lineNumber = 1, len = model.getLineCount(); lineNumber <= len; ++lineNumber) { - const lineContent = model.getLineContent(lineNumber); - - // If line is too long then skip the line - if (lineContent.length > MAX_TOKENIZATION_LINE_LEN) { - continue; - } - - model.forceTokenization(lineNumber); - const lineTokens = model.getLineTokens(lineNumber); - for (let tokenIndex = 0, tokenCount = lineTokens.getCount(); tokenIndex < tokenCount; tokenIndex++) { - const tokenStartOffset = lineTokens.getStartOffset(tokenIndex); - const tokenEndOffset = lineTokens.getEndOffset(tokenIndex); - const tokenType = lineTokens.getStandardTokenType(tokenIndex); - const tokenStr = lineContent.substring(tokenStartOffset, tokenEndOffset); - - // Token is a word and not a comment - if (tokenType === StandardTokenType.Other) { - DEFAULT_WORD_REGEXP.lastIndex = 0; // We assume tokens will usually map 1:1 to words if they match - const wordMatch = DEFAULT_WORD_REGEXP.exec(tokenStr); - - if (wordMatch) { - const word = wordMatch[0]; - if (!this.wordToLineNumbersMap.has(word)) { - this.wordToLineNumbersMap.set(word, []); - } - - this.wordToLineNumbersMap.get(word)!.push(new Position(lineNumber, tokenStartOffset)); - } - } - } - } - } - - return this.wordToLineNumbersMap; - } - dispose(): void { if (this.hoverWidget) { this.hoverWidget.dispose(); From b145d332a9fbef337dba9cda215df866726c262b Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 10 Jan 2020 16:57:04 +0100 Subject: [PATCH 137/843] First cut for DOMLineBreaksComputer --- .../browser/view/domLineBreaksComputer.ts | 212 ++++++++++++++++++ .../editor/browser/widget/codeEditorWidget.ts | 14 +- .../viewModel/monospaceLineBreaksComputer.ts | 7 +- 3 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 src/vs/editor/browser/view/domLineBreaksComputer.ts diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts new file mode 100644 index 00000000000..b22d6757f43 --- /dev/null +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -0,0 +1,212 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ILineBreaksComputerFactory, LineBreakData, ILineBreaksComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { IComputedEditorOptions, EditorOption, WrappingIndent } from 'vs/editor/common/config/editorOptions'; +import { FontInfo } from 'vs/editor/common/config/fontInfo'; +import { createStringBuilder, IStringBuilder } from 'vs/editor/common/core/stringBuilder'; +import { CharCode } from 'vs/base/common/charCode'; +import * as strings from 'vs/base/common/strings'; +import { Configuration } from 'vs/editor/browser/config/configuration'; + +export class DOMLineBreaksComputerFactory implements ILineBreaksComputerFactory { + + public static create(options: IComputedEditorOptions): DOMLineBreaksComputerFactory { + return new DOMLineBreaksComputerFactory( + options.get(EditorOption.fontInfo) + ); + } + + private _fontInfo: FontInfo; + + constructor(fontInfo: FontInfo) { + this._fontInfo = fontInfo; + } + + public createLineBreaksComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineBreaksComputer { + tabSize = tabSize | 0; //@perf + wrappingColumn = +wrappingColumn; //@perf + columnsForFullWidthChar = +columnsForFullWidthChar; //@perf + + let requests: string[] = []; + return { + addRequest: (lineText: string, previousLineBreakData: LineBreakData | null) => { + requests.push(lineText); + }, + finalize: () => { + return createLineBreaks(this._fontInfo, tabSize, wrappingColumn, wrappingIndent, requests); + } + }; + } +} + +function createLineBreaks(fontInfo: FontInfo, tabSize: number, firstLineBreakColumn: number, wrappingIndent: WrappingIndent, requests: string[]): (LineBreakData | null)[] { + const width = Math.round(firstLineBreakColumn * fontInfo.typicalHalfwidthCharacterWidth); + + const containerDomNode = document.createElement('div'); + Configuration.applyFontInfoSlow(containerDomNode, fontInfo); + containerDomNode.style.width = `${width}px`; + + const sb = createStringBuilder(10000); + const charOffsets: number[][] = []; + const visibleColumns: number[][] = []; + for (let i = 0; i < requests.length; i++) { + const r = renderLine(i, requests[i], tabSize, sb); + charOffsets[i] = r[0]; + visibleColumns[i] = r[1]; + } + containerDomNode.innerHTML = sb.build(); + + containerDomNode.style.position = 'absolute'; + containerDomNode.style.right = '0'; + containerDomNode.style.bottom = '0'; + containerDomNode.style.zIndex = '10000'; + document.body.appendChild(containerDomNode); + + let range = document.createRange(); + const lineDomNodes = Array.prototype.slice.call(containerDomNode.children, 0); + + let result: (LineBreakData | null)[] = []; + for (let i = 0; i < requests.length; i++) { + const lineDomNode = lineDomNodes[i]; + result[i] = readLineBreaks(range, lineDomNode, requests[i], charOffsets[i], visibleColumns[i]); + } + + document.body.removeChild(containerDomNode); + return result; +} + +function renderLine(lineIndex: number, lineContent: string, tabSize: number, sb: IStringBuilder): [number[], number[]] { + sb.appendASCIIString('
'); + // if (containsRTL) { + // sb.appendASCIIString('" dir="ltr'); + // } + + let visibleColumn = 0; + let charOffset = 0; + let charOffsets: number[] = []; + let visibleColumns: number[] = []; + + for (let charIndex = 0, len = lineContent.length; charIndex < len; charIndex++) { + charOffsets[charIndex] = charOffset; + visibleColumns[charIndex] = visibleColumn; + const charCode = lineContent.charCodeAt(charIndex); + let producedCharacters = 1; + let charWidth = 1; + switch (charCode) { + case CharCode.Tab: + producedCharacters = (tabSize - (visibleColumn % tabSize)); + charWidth = producedCharacters; + for (let space = 1; space <= producedCharacters; space++) { + sb.appendASCII(CharCode.Space); + // sb.write1(0xA0); //   + } + break; + + case CharCode.Space: + sb.appendASCII(CharCode.Space); + // sb.write1(0xA0); //   + break; + + case CharCode.LessThan: + sb.appendASCIIString('<'); + break; + + case CharCode.GreaterThan: + sb.appendASCIIString('>'); + break; + + case CharCode.Ampersand: + sb.appendASCIIString('&'); + break; + + case CharCode.Null: + sb.appendASCIIString('�'); + break; + + case CharCode.UTF8_BOM: + case CharCode.LINE_SEPARATOR_2028: + sb.write1(0xFFFD); + break; + + default: + if (strings.isFullWidthCharacter(charCode)) { + charWidth++; + } + // if (renderControlCharacters && charCode < 32) { + // sb.write1(9216 + charCode); + // } else { + sb.write1(charCode); + // } + } + + charOffset += producedCharacters; + visibleColumn += charWidth; + } + + charOffsets[lineContent.length] = charOffset; + visibleColumns[lineContent.length] = visibleColumn; + + sb.appendASCIIString('
'); + + return [charOffsets, visibleColumns]; +} + +function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent: string, charOffsets: number[], visibleColumns: number[]): LineBreakData | null { + if (lineContent.length <= 1) { + return null; + } + const textContentNode = lineDomNode.firstChild!; + + const breakOffsets: number[] = []; + discoverBreaks(range, textContentNode, charOffsets, 0, null, lineContent.length - 1, null, breakOffsets); + + if (breakOffsets.length === 0) { + return null; + } + + breakOffsets.push(lineContent.length); + + const breakOffsetsVisibleColumn = []; + for (let i = 0, len = breakOffsets.length; i < len; i++) { + breakOffsetsVisibleColumn[i] = visibleColumns[breakOffsets[i]]; + } + + return new LineBreakData(breakOffsets, breakOffsetsVisibleColumn, 0); +} + +type MaybeRects = ClientRectList | DOMRectList | null; + +function discoverBreaks(range: Range, textContentNode: Node, charOffsets: number[], low: number, lowRects: MaybeRects, high: number, highRects: MaybeRects, result: number[]): void { + if (low === high) { + return; + } + + lowRects = lowRects || readClientRect(range, textContentNode, charOffsets[low], charOffsets[low + 1]); + highRects = highRects || readClientRect(range, textContentNode, charOffsets[high], charOffsets[high + 1]); + + if (Math.abs(lowRects[0].top - highRects[0].top) <= 0.1) { + // same line + return; + } + + // there is at least one line break between these two offsets + if (low + 1 === high) { + // the two characters are adjacent, so the line break must be exactly between them + result.push(high); + return; + } + + const mid = low + ((high - low) / 2) | 0; + const midRects = readClientRect(range, textContentNode, charOffsets[mid], charOffsets[mid + 1]); + discoverBreaks(range, textContentNode, charOffsets, low, lowRects, mid, midRects, result); + discoverBreaks(range, textContentNode, charOffsets, mid, midRects, high, highRects, result); +} + +function readClientRect(range: Range, textContentNode: Node, startOffset: number, endOffset: number): ClientRectList | DOMRectList { + range.setStart(textContentNode, startOffset); + range.setEnd(textContentNode, endOffset); + return range.getClientRects(); +} diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 8015e70e610..d9ac4a31b0d 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -51,8 +51,10 @@ import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/com import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { withNullAsUndefined } from 'vs/base/common/types'; import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; +import { DOMLineBreaksComputerFactory } from 'vs/editor/browser/view/domLineBreaksComputer'; let EDITOR_ID = 0; +const useDOMLineBreaksComputerFactory = false; export interface ICodeEditorWidgetOptions { /** @@ -1337,7 +1339,17 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE model.onBeforeAttached(); - const viewModel = new ViewModel(this._id, this._configuration, model, MonospaceLineBreaksComputerFactory.create(this._configuration.options), (callback) => dom.scheduleAtNextAnimationFrame(callback)); + const viewModel = new ViewModel( + this._id, + this._configuration, + model, + ( + useDOMLineBreaksComputerFactory + ? DOMLineBreaksComputerFactory.create(this._configuration.options) + : MonospaceLineBreaksComputerFactory.create(this._configuration.options) + ), + (callback) => dom.scheduleAtNextAnimationFrame(callback) + ); listenersToRemove.push(model.onDidChangeDecorations((e) => this._onDidChangeModelDecorations.fire(e))); listenersToRemove.push(model.onDidChangeLanguage((e) => { diff --git a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts index dfec62fc04f..546464c2636 100644 --- a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts +++ b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts @@ -51,6 +51,9 @@ class WrappingCharacterClassifier extends CharacterClassifier { } } +let arrPool1: number[] = []; +let arrPool2: number[] = []; + export class MonospaceLineBreaksComputerFactory implements ILineBreaksComputerFactory { public static create(options: IComputedEditorOptions): MonospaceLineBreaksComputerFactory { @@ -88,14 +91,14 @@ export class MonospaceLineBreaksComputerFactory implements ILineBreaksComputerFa result[i] = createLineBreaks(this.classifier, requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); } } + arrPool1.length = 0; + arrPool2.length = 0; return result; } }; } } -let arrPool1: number[] = []; -let arrPool2: number[] = []; function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterClassifier, previousBreakingData: LineBreakData, lineText: string, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): LineBreakData | null { if (firstLineBreakColumn === -1) { return null; From c488a65d9788ee2e355b2bd09a76929e22f3fa6b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 17:07:12 +0100 Subject: [PATCH 138/843] fix rename case, show file type operations --- .../contrib/bulkEdit/browser/bulkEdit.css | 7 +++ .../contrib/bulkEdit/browser/bulkEditPane.ts | 2 +- .../bulkEdit/browser/bulkEditPreview.ts | 17 +++++- .../contrib/bulkEdit/browser/bulkEditTree.ts | 56 +++++++++---------- 4 files changed, 52 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css index b83367eb5d1..52edbe56a36 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css @@ -34,3 +34,10 @@ .monaco-workbench .bulk-edit-panel .monaco-tl-contents .edit-checkbox.disabled { opacity: .5; } + +.monaco-workbench .bulk-edit-panel .monaco-tl-contents .details { + margin-left: .5em; + opacity: .7; + font-size: 0.9em; + white-space: pre +} diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index ee46ddb38ef..c4456386b4f 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -93,7 +93,7 @@ export class BulkEditPane extends ViewPane { this._tree = this._instaService.createInstance( WorkbenchAsyncDataTree, this.id, treeContainer, new BulkEditDelegate(), - [new TextEditElementRenderer(), new FileElementRenderer(resourceLabels)], + [new TextEditElementRenderer(), this._instaService.createInstance(FileElementRenderer, resourceLabels)], this._instaService.createInstance(BulkEditDataSource), { identityProvider: new BulkEditIdentityProvider(), diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index 0414f0d1a27..614c152f94a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -60,6 +60,7 @@ export class BulkFileOperation extends CheckedObject { type = BulkFileOperationType.None; textEdits: BulkTextEdit[] = []; originalEdits = new Map(); + newUri?: URI; constructor( readonly uri: URI, @@ -73,6 +74,9 @@ export class BulkFileOperation extends CheckedObject { this.originalEdits.set(index, edit); if (isResourceTextEdit(edit)) { this.textEdits = this.textEdits.concat(edit.edits.map(edit => new BulkTextEdit(this, edit, this._emitter))); + + } else if (type === BulkFileOperationType.Rename) { + this.newUri = edit.newUri; } } } @@ -105,6 +109,7 @@ export class BulkFileOperations { async _init() { const operationByResource = new Map(); + const newToOldUri = new Map(); for (let idx = 0; idx < this._bulkEdit.edits.length; idx++) { const edit = this._bulkEdit.edits[idx]; @@ -123,6 +128,9 @@ export class BulkFileOperations { // noop -> "soft" rename to something that already exists continue; } + // map newUri onto oldUri so that text-edit appear for + // the same file element + newToOldUri.set(edit.newUri.toString(), uri.toString()); } else if (edit.oldUri) { type = BulkFileOperationType.Delete; @@ -145,8 +153,15 @@ export class BulkFileOperations { continue; } - const key = uri.toString(); + let key = uri.toString(); let operation = operationByResource.get(key); + + // rename + if (!operation && newToOldUri.has(key)) { + key = newToOldUri.get(key)!; + operation = operationByResource.get(key); + } + if (!operation) { operation = new BulkFileOperation(uri, this); operationByResource.set(key, operation); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index b83b83dc92b..0fbdcd26345 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -16,29 +16,18 @@ import { ITextModel } from 'vs/editor/common/model'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TextModel } from 'vs/editor/common/model/textModel'; import { BulkFileOperations, BulkFileOperation, BulkFileOperationType, BulkTextEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; -import { localize } from 'vs/nls'; import { FileKind } from 'vs/platform/files/common/files'; +import { localize } from 'vs/nls'; +import { ILabelService } from 'vs/platform/label/common/label'; // --- VIEW MODEL export class FileElement { - private static _typeLabels: Record = { - [BulkFileOperationType.Create]: localize('create', "create"), - [BulkFileOperationType.Delete]: localize('delete', "delete"), - [BulkFileOperationType.Rename]: localize('rename', "rename"), - [BulkFileOperationType.Create | BulkFileOperationType.Delete]: localize('createDelete', "create & delete"), - [BulkFileOperationType.Create | BulkFileOperationType.Rename]: localize('createRename', "create & rename"), - [BulkFileOperationType.Delete | BulkFileOperationType.Rename]: localize('deleteRename', "delete & rename"), - [BulkFileOperationType.Create | BulkFileOperationType.Delete | BulkFileOperationType.Rename]: localize('createRenameDelete', "create, rename, delete"), - }; - readonly uri: URI; - readonly typeLabel: string; constructor(readonly edit: BulkFileOperation) { this.uri = edit.uri; - this.typeLabel = FileElement._typeLabels[edit.type] || ''; } } @@ -139,8 +128,13 @@ class FileElementTemplate { private readonly _checkbox: HTMLInputElement; private readonly _label: IResourceLabel; + private readonly _details: HTMLSpanElement; - constructor(container: HTMLElement, resourceLabels: ResourceLabels) { + constructor( + container: HTMLElement, + resourceLabels: ResourceLabels, + @ILabelService private readonly _labelService: ILabelService, + ) { this._checkbox = document.createElement('input'); this._checkbox.className = 'edit-checkbox'; @@ -149,6 +143,10 @@ class FileElementTemplate { container.appendChild(this._checkbox); this._label = resourceLabels.create(container, { supportHighlights: true }); + + this._details = document.createElement('span'); + this._details.className = 'details'; + container.appendChild(this._details); } dispose(): void { @@ -162,23 +160,22 @@ class FileElementTemplate { this._localDisposables.add(dom.addDisposableListener(this._checkbox, 'change', (() => element.edit.updateChecked(this._checkbox.checked)))); this._checkbox.checked = element.edit.isChecked(); - const extraClasses: string[] = []; - if (element.edit.type & BulkFileOperationType.Create) { - extraClasses.push('create'); - } - if (element.edit.type & BulkFileOperationType.Delete) { - extraClasses.push('delete'); - } - if (element.edit.type & BulkFileOperationType.Rename) { - extraClasses.push('rename'); - } this._label.setFile(element.uri, { matches: createMatches(score), fileKind: FileKind.FILE, fileDecorations: { colors: true, badges: false }, - // parentCount: element.edit.textEdits.length || undefined, - extraClasses, }); + + // details + if (element.edit.type & BulkFileOperationType.Rename && element.edit.newUri) { + this._details.innerText = localize('detail.rename', "(renaming to {0})", this._labelService.getUriLabel(element.edit.newUri, { relative: true })); + } else if (element.edit.type & BulkFileOperationType.Create) { + this._details.innerText = localize('detail.create', "(creating)"); + } else if (element.edit.type & BulkFileOperationType.Create) { + this._details.innerText = localize('detail.del', "(deleting)"); + } else { + this._details.innerText = ''; + } } } @@ -188,10 +185,13 @@ export class FileElementRenderer implements ITreeRenderer, _index: number, template: FileElementTemplate): void { From f4f3c7ce570aa2ae36f1ed7ac0fc77ab6a5f3326 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 17:10:31 +0100 Subject: [PATCH 139/843] ignore pinned state, close all editors --- .../contrib/bulkEdit/browser/bulkEdit.contribution.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index 2e46f5687aa..19f870dec79 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -83,10 +83,7 @@ class BulkEditPreviewContribution { // (3) close preview editors for (let group of this._editorGroupsService.groups) { for (let input of group.editors) { - if (!group.isPinned(input) - && input instanceof DiffEditorInput - && input.modifiedInput.getResource()?.scheme === BulkEditPreviewProvider.Schema - ) { + if (input instanceof DiffEditorInput && input.modifiedInput.getResource()?.scheme === BulkEditPreviewProvider.Schema) { group.closeEditor(input, { preserveFocus: true }); } } From 0b42d1c01a3a9a0673fc84fd295c1698f01f51ca Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 10 Jan 2020 17:10:20 +0100 Subject: [PATCH 140/843] debug hover: find expression in stack frame tests --- .../contrib/debug/browser/debugHover.ts | 67 +++++++++---------- .../debug/test/browser/debugHover.test.ts | 65 ++++++++++++++++++ 2 files changed, 98 insertions(+), 34 deletions(-) create mode 100644 src/vs/workbench/contrib/debug/test/browser/debugHover.test.ts diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 8e6e499016c..618e02e8b37 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -14,7 +14,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IContentWidget, ICodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IDebugService, IExpression, IExpressionContainer } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IExpression, IExpressionContainer, IStackFrame } from 'vs/workbench/contrib/debug/common/debug'; import { Expression } from 'vs/workbench/contrib/debug/common/debugModel'; import { renderExpressionValue, replaceWhitespace } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; @@ -34,6 +34,34 @@ import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesV const $ = dom.$; const MAX_TREE_HEIGHT = 324; +async function doFindExpression(container: IExpressionContainer, namesToFind: string[]): Promise { + if (!container) { + return Promise.resolve(null); + } + + const children = await container.getChildren(); + // look for our variable in the list. First find the parents of the hovered variable if there are any. + const filtered = children.filter(v => namesToFind[0] === v.name); + if (filtered.length !== 1) { + return null; + } + + if (namesToFind.length === 1) { + return filtered[0]; + } else { + return doFindExpression(filtered[0], namesToFind.slice(1)); + } +} + +export async function findExpressionInStackFrame(stackFrame: IStackFrame, namesToFind: string[]): Promise { + const scopes = await stackFrame.getScopes(); + const nonExpensive = scopes.filter(s => !s.expensive); + const expressions = coalesce(await Promise.all(nonExpensive.map(scope => doFindExpression(scope, namesToFind)))); + + // only show if all expressions found have the same value + return expressions.length > 0 && expressions.every(e => e.value === expressions[0].value) ? expressions[0] : undefined; +} + export class DebugHoverWidget implements IContentWidget { static readonly ID = 'debug.hoverWidget'; @@ -166,7 +194,10 @@ export class DebugHoverWidget implements IContentWidget { expression = new Expression(matchingExpression); await expression.evaluate(session, this.debugService.getViewModel().focusedStackFrame, 'hover'); } else { - expression = await this.findExpressionInStackFrame(coalesce(matchingExpression.split('.').map(word => word.trim()))); + const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; + if (focusedStackFrame) { + expression = await findExpressionInStackFrame(focusedStackFrame, coalesce(matchingExpression.split('.').map(word => word.trim()))); + } } if (!expression || (expression instanceof Expression && !expression.available)) { @@ -186,38 +217,6 @@ export class DebugHoverWidget implements IContentWidget { className: 'hoverHighlight' }); - private async doFindExpression(container: IExpressionContainer, namesToFind: string[]): Promise { - if (!container) { - return Promise.resolve(null); - } - - const children = await container.getChildren(); - // look for our variable in the list. First find the parents of the hovered variable if there are any. - const filtered = children.filter(v => namesToFind[0] === v.name); - if (filtered.length !== 1) { - return null; - } - - if (namesToFind.length === 1) { - return filtered[0]; - } else { - return this.doFindExpression(filtered[0], namesToFind.slice(1)); - } - } - - private async findExpressionInStackFrame(namesToFind: string[]): Promise { - const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; - if (!focusedStackFrame) { - return undefined; - } - - const scopes = await focusedStackFrame.getScopes(); - const nonExpensive = scopes.filter(s => !s.expensive); - const expressions = coalesce(await Promise.all(nonExpensive.map(scope => this.doFindExpression(scope, namesToFind)))); - // only show if all expressions found have the same value - return expressions.length > 0 && expressions.every(e => e.value === expressions[0].value) ? expressions[0] : undefined; - } - private async doShow(position: Position, expression: IExpression, focus: boolean, forceValueHover = false): Promise { if (!this.domNode) { this.create(); diff --git a/src/vs/workbench/contrib/debug/test/browser/debugHover.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugHover.test.ts new file mode 100644 index 00000000000..258fe2a8f3c --- /dev/null +++ b/src/vs/workbench/contrib/debug/test/browser/debugHover.test.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { findExpressionInStackFrame } from 'vs/workbench/contrib/debug/browser/debugHover'; +import { createMockSession } from 'vs/workbench/contrib/debug/test/browser/callStack.test'; +import { StackFrame, Thread, DebugModel, Scope, Variable } from 'vs/workbench/contrib/debug/common/debugModel'; +import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; +import type { IScope, IExpression } from 'vs/workbench/contrib/debug/common/debug'; + +suite('Debug - Hover', () => { + test('find expression in stack frame', async () => { + const model = new DebugModel([], [], [], [], [], { isDirty: (e: any) => false }); + const session = createMockSession(model); + let stackFrame: StackFrame; + + const thread = new class extends Thread { + public getCallStack(): StackFrame[] { + return [stackFrame]; + } + }(session, 'mockthread', 1); + + const firstSource = new Source({ + name: 'internalModule.js', + path: 'a/b/c/d/internalModule.js', + sourceReference: 10, + }, 'aDebugSessionId'); + + let scope: Scope; + stackFrame = new class extends StackFrame { + getScopes(): Promise { + return Promise.resolve([scope]); + } + }(thread, 1, firstSource, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1); + + + let variableA: Variable; + let variableB: Variable; + scope = new class extends Scope { + getChildren(): Promise { + return Promise.resolve([variableA]); + } + }(stackFrame, 1, 'local', 1, false, 10, 10); + + variableA = new class extends Variable { + getChildren(): Promise { + return Promise.resolve([variableB]); + } + }(session, 1, scope, 2, 'A', 'A', undefined!, 0, 0, {}, 'string'); + variableB = new Variable(session, 1, scope, 2, 'B', 'A.B', undefined!, 0, 0, {}, 'string'); + + assert.equal(await findExpressionInStackFrame(stackFrame, ['A']), variableA); + assert.equal(await findExpressionInStackFrame(stackFrame, ['doesNotExist', 'no']), undefined); + assert.equal(await findExpressionInStackFrame(stackFrame, ['a']), undefined); + assert.equal(await findExpressionInStackFrame(stackFrame, ['B']), undefined); + assert.equal(await findExpressionInStackFrame(stackFrame, ['A', 'B']), variableB); + assert.equal(await findExpressionInStackFrame(stackFrame, ['A', 'C']), undefined); + + // We do not search in expensive scopes + scope.expensive = true; + assert.equal(await findExpressionInStackFrame(stackFrame, ['A']), undefined); + }); +}); From 5a50f25375058427df4c74bb2121fa2082f0a896 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 10 Jan 2020 08:30:25 -0800 Subject: [PATCH 141/843] Add "Don't sync this setting" action for settings For #84431 --- .../userDataSync/common/settingsSync.ts | 50 +++++----- .../preferences/browser/settingsTree.ts | 99 +++++++++++++------ 2 files changed, 96 insertions(+), 53 deletions(-) diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 09dd0a4647e..5f21b275d5b 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -183,7 +183,7 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer } if (hasRemoteChanged) { const formatUtils = await this.getFormattingOptions(); - const remoteContent = remoteUserData.content ? computeRemoteContent(content, remoteUserData.content, this.getIgnoredSettings(content), formatUtils) : content; + const remoteContent = remoteUserData.content ? computeRemoteContent(content, remoteUserData.content, getIgnoredSettings(this.configurationService, content), formatUtils) : content; this.logService.info('Settings: Updating remote settings'); const ref = await this.writeToRemote(remoteContent, remoteUserData.ref); remoteUserData = { ref, content }; @@ -242,7 +242,7 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer ) { this.logService.trace('Settings: Merging remote settings with local settings...'); const formatUtils = await this.getFormattingOptions(); - const result = merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null, this.getIgnoredSettings(), resolvedConflicts, formatUtils); + const result = merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null, getIgnoredSettings(this.configurationService), resolvedConflicts, formatUtils); // Sync only if there are changes if (result.hasChanges) { hasLocalChanged = result.mergeContent !== localContent; @@ -276,29 +276,6 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer return this._formattingOptions; } - private getIgnoredSettings(settingsContent?: string): string[] { - let value: string[] = []; - if (settingsContent) { - const setting = parse(settingsContent); - if (setting) { - value = setting['sync.ignoredSettings']; - } - } else { - value = this.configurationService.getValue('sync.ignoredSettings'); - } - const added: string[] = [], removed: string[] = []; - if (Array.isArray(value)) { - for (const key of value) { - if (startsWith(key, '-')) { - removed.push(key.substring(1)); - } else { - added.push(key); - } - } - } - return [...DEFAULT_IGNORED_SETTINGS, ...added].filter(setting => removed.indexOf(setting) === -1); - } - private async getLastSyncUserData(): Promise { try { const content = await this.fileService.readFile(this.lastSyncSettingsResource); @@ -335,3 +312,26 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer } } + +export function getIgnoredSettings(configurationService: IConfigurationService, settingsContent?: string): string[] { + let value: string[] = []; + if (settingsContent) { + const setting = parse(settingsContent); + if (setting) { + value = setting['sync.ignoredSettings']; + } + } else { + value = configurationService.getValue('sync.ignoredSettings'); + } + const added: string[] = [], removed: string[] = []; + if (Array.isArray(value)) { + for (const key of value) { + if (startsWith(key, '-')) { + removed.push(key.substring(1)); + } else { + added.push(key); + } + } + } + return [...DEFAULT_IGNORED_SETTINGS, ...added].filter(setting => removed.indexOf(setting) === -1); +} diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 226f584ad2d..70cbb8822be 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -3,16 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { BrowserFeatures } from 'vs/base/browser/canIUse'; import * as DOM from 'vs/base/browser/dom'; -import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { alert as ariaAlert } from 'vs/base/browser/ui/aria/aria'; import { Button } from 'vs/base/browser/ui/button/button'; import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; -import { ListAriaRootRole, CachedListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { CachedListVirtualDelegate, ListAriaRootRole } from 'vs/base/browser/ui/list/list'; import { DefaultStyleController } from 'vs/base/browser/ui/list/listWidget'; import { ISelectOptionItem, SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; @@ -25,29 +26,29 @@ import { Color, RGBA } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { isIOS } from 'vs/base/common/platform'; import { ISpliceable } from 'vs/base/common/sequence'; import { escapeRegExpCharacters, startsWith } from 'vs/base/common/strings'; +import { isArray } from 'vs/base/common/types'; import { localize } from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, editorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsSync'; import { ITOCEntry } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; import { ISettingsEditorViewState, settingKeyToDisplayFormat, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; -import { ListSettingWidget, IListChangeEvent, IListDataItem, settingsHeaderForeground, settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground, ExcludeSettingWidget } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; +import { ExcludeSettingWidget, IListChangeEvent, IListDataItem, ListSettingWidget, settingsHeaderForeground, settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; -import { ISetting, ISettingsGroup, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { isArray } from 'vs/base/common/types'; -import { BrowserFeatures } from 'vs/base/browser/canIUse'; -import { isIOS } from 'vs/base/common/platform'; +import { ISetting, ISettingsGroup, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; const $ = DOM.$; @@ -203,6 +204,7 @@ interface ISettingItemTemplate extends IDisposableTemplate { deprecationWarningElement: HTMLElement; otherOverridesElement: HTMLElement; toolbar: ToolBar; + elementDisposables: IDisposable[]; } interface ISettingBoolItemTemplate extends ISettingItemTemplate { @@ -300,6 +302,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre // Put common injections back here constructor( private readonly settingActions: IAction[], + private readonly disposableActionFactory: (setting: ISetting) => IAction[], @IThemeService protected readonly _themeService: IThemeService, @IContextViewService protected readonly _contextViewService: IContextViewService, @IOpenerService protected readonly _openerService: IOpenerService, @@ -345,6 +348,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const template: ISettingItemTemplate = { toDispose, + elementDisposables: [], containerElement: container, categoryElement, @@ -393,7 +397,6 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const toolbar = new ToolBar(container, this._contextMenuService, { toggleMenuTitle }); - toolbar.setActions([], this.settingActions)(); const button = container.querySelector('.codicon-more'); if (button) { @@ -410,6 +413,9 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const element = node.element; template.context = element; template.toolbar.context = element; + const actions = this.disposableActionFactory(element.setting); + template.elementDisposables?.push(...actions); + template.toolbar.setActions([], [...this.settingActions, ...actions])(); const setting = element.setting; @@ -455,14 +461,15 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre DOM.append(template.otherOverridesElement, $('span', undefined, ')')); } - DOM.addStandardDisposableListener(view, DOM.EventType.CLICK, (e: IMouseEvent) => { - this._onDidClickOverrideElement.fire({ - targetKey: element.setting.key, - scope: element.overriddenScopeList[i] - }); - e.preventDefault(); - e.stopPropagation(); - }); + template.elementDisposables.push( + DOM.addStandardDisposableListener(view, DOM.EventType.CLICK, (e: IMouseEvent) => { + this._onDidClickOverrideElement.fire({ + targetKey: element.setting.key, + scope: element.overriddenScopeList[i] + }); + e.preventDefault(); + e.stopPropagation(); + })); } } @@ -472,7 +479,6 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre DOM.toggleClass(template.containerElement, 'is-deprecated', !!deprecationText); this.renderValue(element, template, onChange); - } private renderDescriptionMarkdown(element: SettingsTreeSettingElement, text: string, disposeables: DisposableStore): HTMLElement { @@ -563,6 +569,12 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre disposeTemplate(template: IDisposableTemplate): void { dispose(template.toDispose); } + + disposeElement(_element: ITreeNode, _index: number, template: IDisposableTemplate, _height: number | undefined): void { + if ((template as ISettingItemTemplate).elementDisposables) { + dispose((template as ISettingItemTemplate).elementDisposables); + } + } } export class SettingGroupRenderer implements ITreeRenderer { @@ -1113,6 +1125,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre const template: ISettingBoolItemTemplate = { toDispose: [toDispose], + elementDisposables: [], containerElement: container, categoryElement, @@ -1190,15 +1203,16 @@ export class SettingTreeRenderers { this._instantiationService.createInstance(CopySettingAsJSONAction), ]; + const actionFactory = (setting: ISetting) => [this._instantiationService.createInstance(StopSyncingSettingAction, setting)]; const settingRenderers = [ - this._instantiationService.createInstance(SettingBoolRenderer, this.settingActions), - this._instantiationService.createInstance(SettingNumberRenderer, this.settingActions), - this._instantiationService.createInstance(SettingBoolRenderer, this.settingActions), - this._instantiationService.createInstance(SettingArrayRenderer, this.settingActions), - this._instantiationService.createInstance(SettingComplexRenderer, this.settingActions), - this._instantiationService.createInstance(SettingTextRenderer, this.settingActions), - this._instantiationService.createInstance(SettingExcludeRenderer, this.settingActions), - this._instantiationService.createInstance(SettingEnumRenderer, this.settingActions), + this._instantiationService.createInstance(SettingBoolRenderer, this.settingActions, actionFactory), + this._instantiationService.createInstance(SettingNumberRenderer, this.settingActions, actionFactory), + this._instantiationService.createInstance(SettingBoolRenderer, this.settingActions, actionFactory), + this._instantiationService.createInstance(SettingArrayRenderer, this.settingActions, actionFactory), + this._instantiationService.createInstance(SettingComplexRenderer, this.settingActions, actionFactory), + this._instantiationService.createInstance(SettingTextRenderer, this.settingActions, actionFactory), + this._instantiationService.createInstance(SettingExcludeRenderer, this.settingActions, actionFactory), + this._instantiationService.createInstance(SettingEnumRenderer, this.settingActions, actionFactory), ]; this.onDidClickOverrideElement = Event.any(...settingRenderers.map(r => r.onDidClickOverrideElement)); @@ -1596,3 +1610,32 @@ class CopySettingAsJSONAction extends Action { return Promise.resolve(undefined); } } + +class StopSyncingSettingAction extends Action { + static readonly ID = 'settings.stopSyncingSetting'; + static readonly LABEL = localize('stopSyncingSetting', "Don't Sync This Setting"); + + constructor( + private readonly setting: ISetting, + @IConfigurationService private readonly configService: IConfigurationService, + ) { + super(StopSyncingSettingAction.ID, StopSyncingSettingAction.LABEL); + this.update(); + } + + update() { + const ignoredSettings = getIgnoredSettings(this.configService); + this.checked = ignoredSettings.includes(this.setting.key); + } + + async run(): Promise { + const currentValue = this.configService.getValue('sync.ignoredSettings'); + if (this.checked) { + this.configService.updateValue('sync.ignoredSettings', currentValue.filter(v => v !== this.setting.key)); + } else { + this.configService.updateValue('sync.ignoredSettings', [...currentValue, this.setting.key]); + } + + return Promise.resolve(undefined); + } +} From 175ee8056736b46e48b508ac416602a2d2212da5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2020 17:48:24 +0100 Subject: [PATCH 142/843] fix tests --- src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index 9bb9292a4cd..e408f21afd2 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -70,7 +70,11 @@ export class HighlightedLabel { htmlContent += '
'; pos = highlight.end; } - htmlContent += ``; + if (highlight.extraClasses) { + htmlContent += ``; + } else { + htmlContent += ``; + } const substring = this.text.substring(highlight.start, highlight.end); htmlContent += this.supportCodicons ? renderCodicons(escape(substring)) : escape(substring); htmlContent += ''; From 50a4f6e7f816ce997ee423f3297ab772b66ae8df Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 10 Jan 2020 18:04:00 +0100 Subject: [PATCH 143/843] debug: more unit tests for getStackFrameThreadAndSessionToFocus --- .../contrib/debug/browser/debugService.ts | 60 ++++++++-------- .../contrib/debug/browser/debugStatus.ts | 12 ++-- .../debug/test/browser/callStack.test.ts | 69 ++++++++++++++++++- .../debug/test/browser/debugHover.test.ts | 1 + 4 files changed, 106 insertions(+), 36 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 30098061097..5f805dd778b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -727,33 +727,8 @@ export class DebugService implements IDebugService { //---- focus management - async focusStackFrame(stackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): Promise { - if (!session) { - if (stackFrame || thread) { - session = stackFrame ? stackFrame.thread.session : thread!.session; - } else { - const sessions = this.model.getSessions(); - const stoppedSession = sessions.filter(s => s.state === State.Stopped).shift(); - session = stoppedSession || (sessions.length ? sessions[0] : undefined); - } - } - - if (!thread) { - if (stackFrame) { - thread = stackFrame.thread; - } else { - const threads = session ? session.getAllThreads() : undefined; - const stoppedThread = threads && threads.filter(t => t.stopped).shift(); - thread = stoppedThread || (threads && threads.length ? threads[0] : undefined); - } - } - - if (!stackFrame) { - if (thread) { - const callStack = thread.getCallStack(); - stackFrame = first(callStack, sf => !!(sf && sf.source && sf.source.available && sf.source.presentationHint !== 'deemphasize'), undefined); - } - } + async focusStackFrame(_stackFrame: IStackFrame | undefined, _thread?: IThread, _session?: IDebugSession, explicit?: boolean): Promise { + const { stackFrame, thread, session } = getStackFrameThreadAndSessionToFocus(this.model, _stackFrame, _thread, _session); if (stackFrame) { const editor = await stackFrame.openInEditor(this.editorService, true); @@ -1117,3 +1092,34 @@ export class DebugService implements IDebugService { }); } } + +export function getStackFrameThreadAndSessionToFocus(model: IDebugModel, stackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession): { stackFrame: IStackFrame | undefined, thread: IThread | undefined, session: IDebugSession | undefined } { + if (!session) { + if (stackFrame || thread) { + session = stackFrame ? stackFrame.thread.session : thread!.session; + } else { + const sessions = model.getSessions(); + const stoppedSession = sessions.filter(s => s.state === State.Stopped).shift(); + session = stoppedSession || (sessions.length ? sessions[0] : undefined); + } + } + + if (!thread) { + if (stackFrame) { + thread = stackFrame.thread; + } else { + const threads = session ? session.getAllThreads() : undefined; + const stoppedThread = threads && threads.filter(t => t.stopped).shift(); + thread = stoppedThread || (threads && threads.length ? threads[0] : undefined); + } + } + + if (!stackFrame) { + if (thread) { + const callStack = thread.getCallStack(); + stackFrame = first(callStack, sf => !!(sf && sf.source && sf.source.available && sf.source.presentationHint !== 'deemphasize'), undefined); + } + } + + return { session, thread, stackFrame }; +} diff --git a/src/vs/workbench/contrib/debug/browser/debugStatus.ts b/src/vs/workbench/contrib/debug/browser/debugStatus.ts index ac48ec84a4e..8cfbf1702b8 100644 --- a/src/vs/workbench/contrib/debug/browser/debugStatus.ts +++ b/src/vs/workbench/contrib/debug/browser/debugStatus.ts @@ -10,7 +10,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IStatusbarEntry, IStatusbarService, StatusbarAlignment, IStatusbarEntryAccessor } from 'vs/workbench/services/statusbar/common/statusbar'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; - export class DebugStatusContribution implements IWorkbenchContribution { private showInStatusBar!: 'never' | 'always' | 'onFirstSessionStart'; @@ -56,20 +55,17 @@ export class DebugStatusContribution implements IWorkbenchContribution { })); } - private getText(): string { + private get entry(): IStatusbarEntry { + let text = ''; const manager = this.debugService.getConfigurationManager(); const name = manager.selectedConfiguration.name || ''; const nameAndLaunchPresent = name && manager.selectedConfiguration.launch; if (nameAndLaunchPresent) { - return '$(play) ' + (manager.getLaunches().length > 1 ? `${name} (${manager.selectedConfiguration.launch!.name})` : name); + text = '$(play) ' + (manager.getLaunches().length > 1 ? `${name} (${manager.selectedConfiguration.launch!.name})` : name); } - return ''; - } - - private get entry(): IStatusbarEntry { return { - text: this.getText(), + text: text, tooltip: nls.localize('selectAndStartDebug', "Select and start debug configuration"), command: 'workbench.action.debug.selectandstart' }; diff --git a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts index 8442cd4086c..558aa88393d 100644 --- a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts @@ -10,11 +10,12 @@ import { MockRawSession } from 'vs/workbench/contrib/debug/test/common/mockDebug import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; import { Range } from 'vs/editor/common/core/range'; -import { IDebugSessionOptions } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugSessionOptions, State } from 'vs/workbench/contrib/debug/common/debug'; import { NullOpenerService } from 'vs/platform/opener/common/opener'; import { createDecorationsForStackFrame } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution'; import { Constants } from 'vs/base/common/uint'; import { getContext, getContextForContributedActions } from 'vs/workbench/contrib/debug/browser/callStackView'; +import { getStackFrameThreadAndSessionToFocus } from 'vs/workbench/contrib/debug/browser/debugService'; export function createMockSession(model: DebugModel, name = 'mockSession', options?: IDebugSessionOptions): DebugSession { return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!); @@ -349,4 +350,70 @@ suite('Debug - CallStack', () => { contributedContext = getContextForContributedActions(session); assert.equal(contributedContext, session.getId()); }); + + test('focusStackFrameThreadAndSesion', () => { + const threadId1 = 1; + const threadName1 = 'firstThread'; + const threadId2 = 2; + const threadName2 = 'secondThread'; + const stoppedReason = 'breakpoint'; + + // Add the threads + const session = new class extends DebugSession { + get state(): State { + return State.Stopped; + } + }({ resolved: { name: 'stoppedSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!); + + const runningSession = createMockSession(model); + model.addSession(runningSession); + model.addSession(session); + + session['raw'] = rawSession; + + model.rawUpdate({ + sessionId: session.getId(), + threads: [{ + id: threadId1, + name: threadName1 + }] + }); + + // Stopped event with all threads stopped + model.rawUpdate({ + sessionId: session.getId(), + threads: [{ + id: threadId1, + name: threadName1 + }, { + id: threadId2, + name: threadName2 + }], + stoppedDetails: { + reason: stoppedReason, + threadId: 1, + allThreadsStopped: true + }, + }); + + const thread = session.getThread(threadId1)!; + const runningThread = session.getThread(threadId2); + + let toFocus = getStackFrameThreadAndSessionToFocus(model, undefined); + // Verify stopped session and stopped thread get focused + assert.deepEqual(toFocus, { stackFrame: undefined, thread: thread, session: session }); + + toFocus = getStackFrameThreadAndSessionToFocus(model, undefined, undefined, runningSession); + assert.deepEqual(toFocus, { stackFrame: undefined, thread: undefined, session: runningSession }); + + toFocus = getStackFrameThreadAndSessionToFocus(model, undefined, thread); + assert.deepEqual(toFocus, { stackFrame: undefined, thread: thread, session: session }); + + toFocus = getStackFrameThreadAndSessionToFocus(model, undefined, runningThread); + assert.deepEqual(toFocus, { stackFrame: undefined, thread: runningThread, session: session }); + + const stackFrame = new StackFrame(thread, 5, undefined!, 'stackframename2', undefined, undefined!, 1); + toFocus = getStackFrameThreadAndSessionToFocus(model, stackFrame); + assert.deepEqual(toFocus, { stackFrame: stackFrame, thread: thread, session: session }); + }); }); diff --git a/src/vs/workbench/contrib/debug/test/browser/debugHover.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugHover.test.ts index 258fe2a8f3c..241c899eed6 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugHover.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugHover.test.ts @@ -51,6 +51,7 @@ suite('Debug - Hover', () => { }(session, 1, scope, 2, 'A', 'A', undefined!, 0, 0, {}, 'string'); variableB = new Variable(session, 1, scope, 2, 'B', 'A.B', undefined!, 0, 0, {}, 'string'); + assert.equal(await findExpressionInStackFrame(stackFrame, []), undefined); assert.equal(await findExpressionInStackFrame(stackFrame, ['A']), variableA); assert.equal(await findExpressionInStackFrame(stackFrame, ['doesNotExist', 'no']), undefined); assert.equal(await findExpressionInStackFrame(stackFrame, ['a']), undefined); From 98ba603bd8aa4021ec6ecf2b8bd44136577cd8d8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 10 Jan 2020 18:18:43 +0100 Subject: [PATCH 144/843] auto save - directly operate on working copies --- .../browser/parts/editor/editorAutoSave.ts | 14 +++++++++++--- .../workingCopy/common/workingCopyService.ts | 17 +++++++++++------ .../test/common/workingCopyService.test.ts | 4 ++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts index 3a434d37c5d..fd7bbf92a66 100644 --- a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts +++ b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts @@ -7,7 +7,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Disposable, DisposableStore, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { IFilesConfigurationService, AutoSaveMode, IAutoSaveConfiguration } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { SaveReason, IEditorIdentifier, IEditorInput, GroupIdentifier } from 'vs/workbench/common/editor'; +import { SaveReason, IEditorIdentifier, IEditorInput, GroupIdentifier, ISaveOptions } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { withNullAsUndefined } from 'vs/base/common/types'; @@ -95,7 +95,7 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution if (editorIdentifier) { this.editorService.save(editorIdentifier, { reason }); } else { - this.editorService.saveAll({ reason }); + this.saveAllDirty({ reason }); } } } @@ -122,11 +122,19 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution } if (reason) { - this.editorService.saveAll({ reason }); + this.saveAllDirty({ reason }); } } } + private saveAllDirty(options?: ISaveOptions): void { + Promise.all(this.workingCopyService.workingCopies.map(workingCopy => { + if (workingCopy.isDirty() && !(workingCopy.capabilities & WorkingCopyCapabilities.Untitled)) { + workingCopy.save(options); + } + })); + } + private onDidWorkingCopyChangeDirty(workingCopy: IWorkingCopy): void { if (typeof this.autoSaveAfterDelay !== 'number') { return; // auto save after delay must be enabled diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index d7a2b403a5f..e79533e7333 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -8,7 +8,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { Disposable, IDisposable, toDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; -import { TernarySearchTree } from 'vs/base/common/map'; +import { TernarySearchTree, values } from 'vs/base/common/map'; import { ISaveOptions } from 'vs/workbench/common/editor'; export const enum WorkingCopyCapabilities { @@ -50,6 +50,7 @@ export interface IWorkingCopyService { _serviceBrand: undefined; + //#region Dirty Tracking readonly onDidChangeDirty: Event; @@ -65,6 +66,8 @@ export interface IWorkingCopyService { //#region Registry + readonly workingCopies: IWorkingCopy[]; + registerWorkingCopy(workingCopy: IWorkingCopy): IDisposable; //#endregion @@ -93,7 +96,7 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic } get hasDirty(): boolean { - for (const workingCopy of this.workingCopies) { + for (const workingCopy of this._workingCopies) { if (workingCopy.isDirty()) { return true; } @@ -105,7 +108,7 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic get dirtyCount(): number { let totalDirtyCount = 0; - for (const workingCopy of this.workingCopies) { + for (const workingCopy of this._workingCopies) { if (workingCopy.isDirty()) { totalDirtyCount++; } @@ -120,7 +123,9 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic //#region Registry private mapResourceToWorkingCopy = TernarySearchTree.forPaths>(); - private workingCopies = new Set(); + + get workingCopies(): IWorkingCopy[] { return values(this._workingCopies); } + private _workingCopies = new Set(); registerWorkingCopy(workingCopy: IWorkingCopy): IDisposable { const disposables = new DisposableStore(); @@ -134,7 +139,7 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic workingCopiesForResource.add(workingCopy); - this.workingCopies.add(workingCopy); + this._workingCopies.add(workingCopy); // Dirty Events disposables.add(workingCopy.onDidChangeDirty(() => this._onDidChangeDirty.fire(workingCopy))); @@ -156,7 +161,7 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic this.mapResourceToWorkingCopy.delete(workingCopy.resource.toString()); } - this.workingCopies.delete(workingCopy); + this._workingCopies.delete(workingCopy); // If copy is dirty, ensure to fire an event to signal the dirty change // (a disposed working copy cannot account for being dirty in our model) diff --git a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts index 4bac643a90d..b929447efd9 100644 --- a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts @@ -61,6 +61,7 @@ suite('WorkingCopyService', () => { assert.equal(service.hasDirty, false); assert.equal(service.dirtyCount, 0); + assert.equal(service.workingCopies.length, 0); assert.equal(service.isDirty(URI.file('/')), false); // resource 1 @@ -68,6 +69,7 @@ suite('WorkingCopyService', () => { const copy1 = new TestWorkingCopy(resource1); const unregister1 = service.registerWorkingCopy(copy1); + assert.equal(service.workingCopies.length, 1); assert.equal(service.dirtyCount, 0); assert.equal(service.isDirty(resource1), false); assert.equal(service.hasDirty, false); @@ -90,6 +92,8 @@ suite('WorkingCopyService', () => { unregister1.dispose(); + assert.equal(service.workingCopies.length, 0); + // resource 2 const resource2 = URI.file('/some/folder/file-dirty.txt'); const copy2 = new TestWorkingCopy(resource2, true); From 76d0b308ededd3a706d26fb1ccc564428248cd29 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 10 Jan 2020 18:27:10 +0100 Subject: [PATCH 145/843] import :lipstick: --- src/vs/workbench/common/editor/resourceEditorInput.ts | 2 +- .../services/workingCopy/test/common/workingCopyService.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index c7f8bbbcb3c..c97918ed39a 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -10,7 +10,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { basename } from 'vs/base/common/resources'; import { ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import type { IEditorViewState } from 'vs/editor/common/editorCommon'; +import { IEditorViewState } from 'vs/editor/common/editorCommon'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; /** diff --git a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts index b929447efd9..ee32a833262 100644 --- a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { TestWorkingCopyService } from 'vs/workbench/test/workbenchTestServices'; -import type { ISaveOptions } from 'vs/workbench/common/editor'; +import { ISaveOptions } from 'vs/workbench/common/editor'; suite('WorkingCopyService', () => { From 438c35c042b9d114244c7014528a4217c1bea78c Mon Sep 17 00:00:00 2001 From: Luis Oliveira Date: Fri, 10 Jan 2020 17:59:06 +0000 Subject: [PATCH 146/843] Applying changes for shadow DOM --- src/vs/base/browser/dom.ts | 19 +++-- src/vs/base/browser/keyboardEvent.ts | 2 +- src/vs/base/browser/mouseEvent.ts | 2 +- .../editor/browser/controller/mouseTarget.ts | 5 +- src/vs/editor/browser/editorDom.ts | 84 +++++++++++++++++++ 5 files changed, 104 insertions(+), 8 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index b7a55a4ca64..5b48ad6478d 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -810,7 +810,7 @@ export function findParentWithClass(node: HTMLElement, clazz: string, stopAtClaz } } - node = node.parentNode; + node = node.parentNode || (node as any).host; } return null; @@ -820,7 +820,16 @@ export function hasParentWithClass(node: HTMLElement, clazz: string, stopAtClazz return !!findParentWithClass(node, clazz, stopAtClazzOrNode); } -export function createStyleSheet(container: HTMLElement = document.getElementsByTagName('head')[0]): HTMLStyleElement { +export function createStyleSheet(container: HTMLElement): HTMLStyleElement { + if (!container) { + if ((window as any).monacoShadowRoot) { + container = (window as any).monacoShadowRoot.querySelector('head'); + } + else { + container = document.getElementsByTagName('head')[0] + } + } + let style = document.createElement('style'); style.type = 'text/css'; style.media = 'screen'; @@ -979,7 +988,7 @@ export function saveParentsScrollTop(node: Element): number[] { let r: number[] = []; for (let i = 0; node && node.nodeType === node.ELEMENT_NODE; i++) { r[i] = node.scrollTop; - node = node.parentNode; + node = node.parentNode || (node as any).host; } return r; } @@ -989,7 +998,7 @@ export function restoreParentsScrollTop(node: Element, state: number[]): void { if (node.scrollTop !== state[i]) { node.scrollTop = state[i]; } - node = node.parentNode; + node = node.parentNode || (node as any).host; } } @@ -1172,7 +1181,7 @@ function findParentWithAttribute(node: Node | null, attribute: string): HTMLElem return node; } - node = node.parentNode; + node = node.parentNode || (node as any).host; } return null; diff --git a/src/vs/base/browser/keyboardEvent.ts b/src/vs/base/browser/keyboardEvent.ts index 03bdffc95ed..1f312c6fae1 100644 --- a/src/vs/base/browser/keyboardEvent.ts +++ b/src/vs/base/browser/keyboardEvent.ts @@ -228,7 +228,7 @@ export class StandardKeyboardEvent implements IKeyboardEvent { let e = source; this.browserEvent = e; - this.target = e.target; + this.target = (e as any).path ? (e as any).path[0] : e.target; this.ctrlKey = e.ctrlKey; this.shiftKey = e.shiftKey; diff --git a/src/vs/base/browser/mouseEvent.ts b/src/vs/base/browser/mouseEvent.ts index f3f2089100b..082a458f2f2 100644 --- a/src/vs/base/browser/mouseEvent.ts +++ b/src/vs/base/browser/mouseEvent.ts @@ -50,7 +50,7 @@ export class StandardMouseEvent implements IMouseEvent { this.middleButton = e.button === 1; this.rightButton = e.button === 2; - this.target = e.target; + this.target = (e as any).path ? (e as any).path[0] : e.target; this.detail = e.detail || 1; if (e.type === 'dblclick') { diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index c38191a19c6..3e75b7fa835 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -836,7 +836,10 @@ export class MouseTargetFactory { private static _actualDoHitTestWithCaretRangeFromPoint(ctx: HitTestContext, coords: ClientCoordinates): IHitTestResult { - const range: Range = document.caretRangeFromPoint(coords.clientX, coords.clientY); + const docOrRoot = (window as any).monacoShadowRoot || document; + const range: Range = docOrRoot.caretRangeFromPoint(coords.clientX, coords.clientY); + + //const range: Range = document.caretRangeFromPoint(coords.clientX, coords.clientY); if (!range || !range.startContainer) { return { diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 9401fdd259e..1fd10f1932d 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -205,3 +205,87 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { }); } } + + +// Dynamically polyfill the `caretRangeFromPoint` method +if (typeof ShadowRoot.prototype.caretRangeFromPoint === 'undefined') { + /** + * The `best I can do` polyfill for this method + */ + ShadowRoot.prototype.caretRangeFromPoint = function (x, y) { + + let range = document.createRange(); + + // Get the element under the point + let el:Element | null = this.elementFromPoint(x, y); + + if (el !== null) { + // Get the last child of the element until its firstChild is a text node + // This assumes that the pointer is on the right of the line, out of the tokens + // and that we want to get the offset of the last token of the line + while ((el as any).firstChild.nodeType !== (el as any).firstChild.TEXT_NODE) { + el = (el as any).lastChild; + } + + // Grab its rect + let rect = (el as any).getBoundingClientRect(); + + // And its font + let font = window.getComputedStyle(el as any, null).getPropertyValue('font'); + + // And also its txt content + let text = (el as any).innerText; + + // Position the pixel cursor at the left of the element + let pixelCursor = rect.left; + let offset = 0; + let step; + + // If the point is on the right of the box put the cursor after the last character + if (x > rect.left + rect.width) { + offset = text.length; + } else { + // Goes through all the characters of the innerText, and checks if the x of the point + // belongs to the character. + for (let i = 0; i < text.length + 1; i++) { + // The step is half the width of the character + step = (window as any)._getCharWidth(text.charAt(i), font) / 2; + // Move to the center of the character + pixelCursor += step; + // If the x of the point is smaller that the position of the cursor, the point is over that character + if (x < pixelCursor) { + offset = i; + break; + } + // Move between the current character and the next + pixelCursor += step; + } + } + + // Creates a range with the text node of the element and set the offset found + range.setStart((el as any).firstChild, offset); + range.setEnd((el as any).firstChild, offset); + } + + return range; + }; +} + +(function (window) { + (window as any)._getCharWidth = (char:any, font:any) => { + let cacheKey = char + font; + if ((window as any)._getCharWidth._cache[cacheKey]) { + return (window as any)._getCharWidth._cache[cacheKey]; + } + + let context = (window as any)._getCharWidth._canvas.getContext("2d"); + context.font = font; + let metrics = context.measureText(char); + let width = metrics.width; + (window as any)._getCharWidth._cache[cacheKey] = width; + return width; + } + + (window as any)._getCharWidth._cache = {}; + (window as any)._getCharWidth._canvas = document.createElement('canvas'); +})(window); From 35d37c78f24ac2ea44a78bc60eb914f6e3b5cd97 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 10 Jan 2020 19:12:56 +0100 Subject: [PATCH 147/843] #85216 Improve settings sync UX --- .../userDataSync/common/userDataSync.ts | 13 ++ .../userDataSync/browser/userDataSync.ts | 144 +++++++++++++++--- 2 files changed, 135 insertions(+), 22 deletions(-) diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index ca76015a885..6ee18e5c756 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -28,6 +28,19 @@ export const DEFAULT_IGNORED_SETTINGS = [ 'sync.enableExtensions', ]; +export interface ISyncConfiguration { + sync: { + enable: boolean, + enableSettings: boolean, + enableKeybindings: boolean, + enableUIState: boolean, + enableExtensions: boolean, + keybindingsPerPlatform: boolean, + ignoredExtensions: string[], + ignoredSettings: string[] + } +} + export function registerConfiguration(): IDisposable { const ignoredSettingsSchemaId = 'vscode://schemas/ignoredSettings'; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 77789580a70..07264e336bd 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, IUserDataSyncStore, registerConfiguration, getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, IUserDataSyncStore, registerConfiguration, getUserDataSyncStore, ISyncConfiguration } from 'vs/platform/userDataSync/common/userDataSync'; import { localize } from 'vs/nls'; import { Disposable, MutableDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -138,7 +138,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo const enabled = this.configurationService.getValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING); if (enabled) { if (this.authTokenService.status === AuthTokenStatus.SignedOut) { - const handle = this.notificationService.prompt(Severity.Info, localize('ask to sign in', "Please sign in with your {0} account to sync configuration across all your machines", this.userDataSyncStore!.account), + const handle = this.notificationService.prompt(Severity.Info, this.getSignInAndTurnOnDetailString(), [ { label: localize('Sign in', "Sign in"), @@ -179,25 +179,123 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } } + private getTurnOnDetailString(): string { + const { enableSettings, enableKeybindings, enableExtensions, enableUIState } = this.configurationService.getValue<{ enableSettings: boolean, enableKeybindings: boolean, enableExtensions: boolean, enableUIState: boolean }>('sync'); + if (enableSettings && enableKeybindings && enableExtensions && enableUIState) { + return localize('turn on sync detail 1', "This will synchronize your settings, keybindings, extensions and other UI state (display language) across all your devices."); + } + if (enableSettings && enableKeybindings && enableExtensions) { + return localize('turn on sync detail 2', "This will synchronize your settings, keybindings and extensions across all your devices."); + } + if (enableSettings && enableKeybindings && enableUIState) { + return localize('turn on sync detail 3', "This will synchronize your settings, keybindings and other UI state (display language) across all your devices."); + } + if (enableSettings && enableExtensions && enableUIState) { + return localize('turn on sync detail 4', "This will synchronize your settings, extensions and other UI state (display language) across all your devices."); + } + if (enableSettings && enableKeybindings) { + return localize('turn on sync detail 5', "This will synchronize your settings and keybindings across all your devices."); + } + if (enableSettings && enableExtensions) { + return localize('turn on sync detail 6', "This will synchronize your settings and extensions across all your devices."); + } + if (enableSettings && enableUIState) { + return localize('turn on sync detail 7', "This will synchronize your settings and UI state (display language) across all your devices."); + } + if (enableKeybindings && enableExtensions) { + return localize('turn on sync detail 8', "This will synchronize your keybindings and extensions across all your devices."); + } + if (enableKeybindings && enableUIState) { + return localize('turn on sync detail 9', "This will synchronize your keybindings and UI state (display language) across all your devices."); + } + if (enableExtensions && enableUIState) { + return localize('turn on sync detail 10', "This will synchronize your extensions and UI state (display language) across all your devices."); + } + if (enableSettings) { + return localize('turn on sync detail 11', "This will synchronize your settings across all your devices."); + } + if (enableKeybindings) { + return localize('turn on sync detail 12', "This will synchronize your keybindings across all your devices."); + } + if (enableExtensions) { + return localize('turn on sync detail 13', "This will synchronize your extensions across all your devices."); + } + if (enableUIState) { + return localize('turn on sync detail 14', "This will synchronize your UI state (display language) across all your devices."); + } + return ''; + } + + private getSignInAndTurnOnDetailString(): string { + const { enableSettings, enableKeybindings, enableExtensions, enableUIState } = this.configurationService.getValue().sync; + if (enableSettings && enableKeybindings && enableExtensions && enableUIState) { + return localize('sign in and turn on sync detail 1', "Please sign in with your {0} account to synchronize your settings, keybindings, extensions and other UI state (display language) across all your devices.", this.userDataSyncStore!.account); + } + if (enableSettings && enableKeybindings && enableExtensions) { + return localize('sign in and turn on sync detail 2', "Please sign in with your {0} account to synchronize your settings, keybindings and extensions across all your devices.", this.userDataSyncStore!.account); + } + if (enableSettings && enableKeybindings && enableUIState) { + return localize('sign in and turn on sync detail 3', "Please sign in with your {0} account to synchronize your settings, keybindings and other UI state (display language) across all your devices.", this.userDataSyncStore!.account); + } + if (enableSettings && enableExtensions && enableUIState) { + return localize('sign in and turn on sync detail 4', "Please sign in with your {0} account to synchronize your settings, extensions and other UI state (display language) across all your devices.", this.userDataSyncStore!.account); + } + if (enableSettings && enableKeybindings) { + return localize('sign in and turn on sync detail 5', "Please sign in with your {0} account to synchronize your settings and keybindings across all your devices.", this.userDataSyncStore!.account); + } + if (enableSettings && enableExtensions) { + return localize('sign in and turn on sync detail 6', "Please sign in with your {0} account to synchronize your settings and extensions across all your devices.", this.userDataSyncStore!.account); + } + if (enableSettings && enableUIState) { + return localize('sign in and turn on sync detail 7', "Please sign in with your {0} account to synchronize your settings and UI state (display language) across all your devices.", this.userDataSyncStore!.account); + } + if (enableKeybindings && enableExtensions) { + return localize('sign in and turn on sync detail 8', "Please sign in with your {0} account to synchronize your keybindings and extensions across all your devices.", this.userDataSyncStore!.account); + } + if (enableKeybindings && enableUIState) { + return localize('sign in and turn on sync detail 9', "Please sign in with your {0} account to synchronize your keybindings and UI state (display language) across all your devices.", this.userDataSyncStore!.account); + } + if (enableExtensions && enableUIState) { + return localize('sign in and turn on sync detail 10', "Please sign in with your {0} account to synchronize your extensions and UI state (display language) across all your devices.", this.userDataSyncStore!.account); + } + if (enableSettings) { + return localize('sign in and turn on sync detail 11', "Please sign in with your {0} account to synchronize your settings across all your devices.", this.userDataSyncStore!.account); + } + if (enableKeybindings) { + return localize('sign in and turn on sync detail 12', "Please sign in with your {0} account to synchronize your keybindings across all your devices.", this.userDataSyncStore!.account); + } + if (enableExtensions) { + return localize('sign in and turn on sync detail 13', "Please sign in with your {0} account to synchronize your extensions across all your devices.", this.userDataSyncStore!.account); + } + if (enableUIState) { + return localize('sign in and turn on sync detail 14', "Please sign in with your {0} account to synchronize your UI state (display language) across all your devices.", this.userDataSyncStore!.account); + } + return ''; + } + private async turnOn(): Promise { - if (this.authTokenService.status === AuthTokenStatus.SignedOut) { - const result = await this.dialogService.confirm({ - type: 'info', - message: localize('sign in to account', "Sign in to {0}", this.userDataSyncStore!.name), - detail: localize('ask to sign in', "Please sign in with your {0} account to sync configuration across all your machines", this.userDataSyncStore!.account), - primaryButton: localize('Sign in', "Sign in") - }); - if (!result.confirmed) { - return; - } + const message = localize('turn on sync', "Turn on Sync"); + let detail: string, primaryButton: string; + if (this.authTokenService.status === AuthTokenStatus.SignedIn) { + detail = this.getTurnOnDetailString(); + primaryButton = localize('turn on sync', "Turn on Sync"); + } else { + detail = this.getSignInAndTurnOnDetailString(); + primaryButton = localize('sign in and turn on sync', "Sign in & Turn on Sync"); + } + const result = await this.dialogService.show(Severity.Info, message, [primaryButton, localize('cancel', "Cancel"), localize('configure', "Configure")], { detail, cancelId: 1 }); + switch (result.choice) { + case 1: return; + case 2: await this.configureSyncOptions(); return this.turnOn(); + } + if (this.authTokenService.status !== AuthTokenStatus.SignedIn) { await this.signIn(); } - await this.configureSyncOptions(); await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, true); this.notificationService.info(localize('Sync Started', "Sync Started.")); } - private async configureSyncOptions(): Promise { + private async configureSyncOptions(): Promise { return new Promise((c, e) => { const disposables: DisposableStore = new DisposableStore(); const quickPick = this.quickInputService.createQuickPick(); @@ -222,15 +320,17 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }]; quickPick.items = items; quickPick.selectedItems = items.filter(item => this.configurationService.getValue(item.id)); - disposables.add(quickPick.onDidAccept(() => { - for (const item of items) { - const wasEnabled = this.configurationService.getValue(item.id); - const isEnabled = !!quickPick.selectedItems.filter(selected => selected.id === item.id)[0]; - if (wasEnabled !== isEnabled) { - this.configurationService.updateValue(item.id!, isEnabled); + disposables.add(quickPick.onDidAccept(async () => { + if (quickPick.selectedItems.length) { + for (const item of items) { + const wasEnabled = this.configurationService.getValue(item.id); + const isEnabled = !!quickPick.selectedItems.filter(selected => selected.id === item.id)[0]; + if (wasEnabled !== isEnabled) { + await this.configurationService.updateValue(item.id!, isEnabled); + } } + quickPick.hide(); } - quickPick.hide(); })); disposables.add(quickPick.onDidHide(() => { disposables.dispose(); @@ -333,7 +433,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: turnOnSyncCommandId, - title: localize('turn on sync', "Sync: Turn on sync...") + title: localize('turn on sync...', "Sync: Turn on sync...") }, when: turnOnSyncWhenContext, }); From 7879818f9c54aa63b9887ed96f53671d29bbc3fe Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 10 Jan 2020 10:17:45 -0800 Subject: [PATCH 148/843] Fix circular dependency issue --- .../search/browser/search.contribution.ts | 3 +- .../contrib/search/browser/searchActions.ts | 2 +- .../contrib/search/browser/searchEditor.ts | 416 +---------------- .../search/browser/searchEditorCommands.ts | 434 ++++++++++++++++++ .../contrib/search/browser/searchView.ts | 2 +- 5 files changed, 444 insertions(+), 413 deletions(-) create mode 100644 src/vs/workbench/contrib/search/browser/searchEditorCommands.ts diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index ab59270e380..2e499c9e06d 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -57,7 +57,8 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { assertType } from 'vs/base/common/types'; import { SearchViewPaneContainer } from 'vs/workbench/contrib/search/browser/searchViewlet'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; -import { SearchEditor, SearchEditorInput } from 'vs/workbench/contrib/search/browser/searchEditor'; +import { SearchEditorInput } from 'vs/workbench/contrib/search/browser/searchEditorCommands'; +import { SearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 3be9ac82737..2839f775568 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -29,7 +29,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { SearchViewPaneContainer } 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, refreshActiveEditorSearch, openNewSearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor'; +import { createEditorFromSearchResult, refreshActiveEditorSearch, openNewSearchEditor } from 'vs/workbench/contrib/search/browser/searchEditorCommands'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; diff --git a/src/vs/workbench/contrib/search/browser/searchEditor.ts b/src/vs/workbench/contrib/search/browser/searchEditor.ts index 83b746109a5..8e7e247f519 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditor.ts @@ -5,21 +5,16 @@ import * as DOM from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { coalesce, flatten } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import * as network from 'vs/base/common/network'; -import { repeat } from 'vs/base/common/strings'; import { assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/searchEditor'; -import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; import type { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; -import { EndOfLinePreference, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { TrackedRangeStickiness } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { IModeService } from 'vs/editor/common/services/modeService'; import { localize } from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -28,84 +23,25 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILabelService } from 'vs/platform/label/common/label'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { searchEditorFindMatch, searchEditorFindMatchBorder } from 'vs/platform/theme/common/colorRegistry'; -import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; -import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput'; import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget'; import { SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget'; import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search'; -import { FileMatch, Match, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { SearchModel } from 'vs/workbench/contrib/search/common/searchModel'; import { IPatternInfo, ISearchConfigurationProperties, ITextQuery } from 'vs/workbench/services/search/common/search'; import { Delayer } from 'vs/base/common/async'; +import { serializeSearchResultForEditor, SearchConfiguration, SearchEditorInput } from 'vs/workbench/contrib/search/browser/searchEditorCommands'; const RESULT_LINE_REGEX = /^(\s+)(\d+)(:| )(\s+)(.*)$/; -type SearchConfiguration = { - query: string, - includes: string, - excludes: string, - contextLines: number, - wholeWord: boolean, - caseSensitive: boolean, - regexp: boolean, - useIgnores: boolean -}; +// Using \r\n on Windows inserts an extra newline between results. +const lineDelimiter = '\n'; -let searchEditorInputInstances = 0; -export class SearchEditorInput extends EditorInput { - static readonly ID: string = 'workbench.editorinputs.searchEditorInput'; - public config: SearchConfiguration; - private instanceNumber: number = searchEditorInputInstances++; - - constructor( - config: SearchConfiguration | undefined, - @IModelService private readonly modelService: IModelService, - @IModeService private readonly modeService: IModeService, - ) { - super(); - - if (config === undefined) { - this.config = { query: '', includes: '', excludes: '', contextLines: 0, wholeWord: false, caseSensitive: false, regexp: false, useIgnores: true }; - } else { - this.config = config; - } - - const searchResultMode = this.modeService.create('search-result'); - this.modelService.createModel('', searchResultMode, this.getResource()); - } - - getTypeId(): string { - return SearchEditorInput.ID; - } - - getResource(): URI { - return URI.from({ scheme: 'code-search', fragment: `${this.instanceNumber}` }); - } - - getName(): string { - return this.config.query ? localize('searchTitle.withQuery', "Search: {0}", this.config.query) : localize('searchTitle', "Search"); - } - - setConfig(config: SearchConfiguration) { - this.config = config; - this._onDidChangeLabel.fire(); - } - - async resolve() { - return null; - } - - dispose() { - this.modelService.destroyModel(this.getResource()); - super.dispose(); - } -} export class SearchEditor extends BaseEditor { static readonly ID: string = 'workbench.editor.searchEditor'; @@ -353,343 +289,3 @@ export class SearchEditor extends BaseEditor { return this.searchResultEditor.getModel(); } } - -// 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 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).replace(/\r?\n?$/, ''); - - 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; -}; - -type SearchResultSerialization = { text: string[], matchRanges: Range[] }; - -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 context: { line: string, lineNumber: number }[] = []; - fileMatch.context.forEach((line, lineNumber) => context.push({ line, lineNumber })); - context.sort((a, b) => a.lineNumber - b.lineNumber); - - let lastLine: number | undefined = undefined; - - const seenLines = new Set(); - serializedMatches.forEach(match => { - if (!seenLines.has(match.line)) { - while (context.length && context[0].lineNumber < +match.lineNumber) { - const { line, lineNumber } = context.shift()!; - if (lastLine !== undefined && lineNumber !== lastLine + 1) { - text.push(''); - } - text.push(` ${lineNumber} ${line}`); - lastLine = lineNumber; - } - - targetLineNumberToOffset[match.lineNumber] = text.length; - seenLines.add(match.line); - text.push(match.line); - lastLine = +match.lineNumber; - } - - matchRanges.push(...match.ranges.map(translateRangeLines(targetLineNumberToOffset[match.lineNumber]))); - }); - - while (context.length) { - const { line, lineNumber } = context.shift()!; - text.push(` ${lineNumber} ${line}`); - } - - 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 }; -}; - -const contentPatternToSearchResultHeader = (pattern: ITextQuery | null, includes: string, excludes: string, contextLines: number): 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 || pattern.userDisabledExcludesAndIgnoreFiles) - && `# Flags: ${coalesce([ - pattern.contentPattern.isCaseSensitive && 'CaseSensitive', - pattern.contentPattern.isWordMatch && 'WordMatch', - pattern.contentPattern.isRegExp && 'RegExp', - pattern.userDisabledExcludesAndIgnoreFiles && 'IgnoreExcludeSettings' - ]).join(' ')}`, - includes ? `# Including: ${includes}` : undefined, - excludes ? `# Excluding: ${excludes}` : undefined, - contextLines ? `# ContextLines: ${contextLines}` : undefined, - '' - ]); -}; - - -type SearchHeader = { - pattern: string; - flags: { - regex: boolean; - wholeWord: boolean; - caseSensitive: boolean; - ignoreExcludes: boolean; - }; - includes: string; - excludes: string; - context: number | undefined; -}; - -const searchHeaderToContentPattern = (header: string[]): SearchHeader => { - const query: SearchHeader = { - pattern: '', - flags: { regex: false, caseSensitive: false, ignoreExcludes: false, wholeWord: false }, - includes: '', - excludes: '', - context: undefined - }; - - const unescapeNewlines = (str: string) => { - let out = ''; - for (let i = 0; i < str.length; i++) { - if (str[i] === '\\') { - i++; - const escaped = str[i]; - - if (escaped === 'n') { - out += '\n'; - } - else if (escaped === '\\') { - out += '\\'; - } - else { - throw Error(localize('invalidQueryStringError', "All backslashes in Query string must be escaped (\\\\)")); - } - } else { - out += str[i]; - } - } - return out; - }; - 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 'ContextLines': query.context = +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, contextLines: number, labelFormatter: (x: URI) => string, includeHeader: boolean): SearchResultSerialization => { - const header = includeHeader ? contentPatternToSearchResultHeader(searchResult.query, rawIncludePattern, rawExcludePattern, contextLines) : []; - const allResults = - flattenSearchResultSerializations( - 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 (contextLines: number | undefined, editorService: IEditorService, instantiationService: IInstantiationService, contextService: IWorkspaceContextService, labelService: ILabelService, configurationService: IConfigurationService) => { - const editorWidget = editorService.activeTextEditorWidget; - if (!isCodeEditor(editorWidget)) { - return; - } - - const textModel = editorWidget.getModel(); - if (!textModel) { return; } - - const header = textModel.getValueInRange(new Range(1, 1, 5, 1), EndOfLinePreference.LF) - .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 - }; - - contextLines = contextLines ?? contentPattern.context ?? 0; - - 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 - }, - afterContext: contextLines, - beforeContext: contextLines, - 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, contentPattern.includes, contentPattern.excludes, contextLines, labelFormatter, false); - - textModel.setValue(results.text.join(lineDelimiter)); - textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); - }; - -export const openNewSearchEditor = - async (editorService: IEditorService, instantiationService: IInstantiationService) => { - await editorService.openEditor(instantiationService.createInstance(SearchEditorInput, undefined), { pinned: true }); - }; - -export const createEditorFromSearchResult = - async (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, labelService: ILabelService, editorService: IEditorService, instantiationService: IInstantiationService) => { - if (!searchResult.query) { - console.error('Expected searchResult.query to be defined. Got', searchResult); - return; - } - - 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, 0, labelFormatter, false); - const contents = results.text.join(lineDelimiter); - let possible = { - contents, - mode: 'search-result', - resource: URI.from({ scheme: network.Schemas.untitled, path: searchTerm }) - }; - - let id = 0; - - let existing = editorService.getOpened(possible); - while (existing) { - if (existing instanceof UntitledTextEditorInput) { - const model = await existing.resolve(); - const existingContents = model.textEditorModel.getValue(EndOfLinePreference.LF); - if (existingContents === contents) { - break; - } - } - possible.resource = possible.resource.with({ path: searchTerm + '-' + ++id }); - existing = editorService.getOpened(possible); - } - - const editor = await editorService.openEditor( - instantiationService.createInstance( - SearchEditorInput, - { - query: searchResult.query.contentPattern.pattern, - regexp: !!searchResult.query.contentPattern.isRegExp, - caseSensitive: !!searchResult.query.contentPattern.isCaseSensitive, - wholeWord: !!searchResult.query.contentPattern.isWordMatch, - includes: rawIncludePattern, - excludes: rawExcludePattern, - contextLines: 0, - useIgnores: !searchResult.query.userDisabledExcludesAndIgnoreFiles, - }), - { pinned: true }) as SearchEditor; - - const model = assertIsDefined(editor.getModel()); - model.setValue(contents); - model.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); - }; - -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; }`); - } -}); diff --git a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts new file mode 100644 index 00000000000..d6735a62c12 --- /dev/null +++ b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts @@ -0,0 +1,434 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { coalesce, flatten } from 'vs/base/common/arrays'; +import * as network from 'vs/base/common/network'; +import { repeat } from 'vs/base/common/strings'; +import { assertIsDefined } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import 'vs/css!./media/searchEditor'; +import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { Range } from 'vs/editor/common/core/range'; +import { EndOfLinePreference, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { searchEditorFindMatch, searchEditorFindMatchBorder } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput'; +import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; +import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search'; +import { FileMatch, Match, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IPatternInfo, ISearchConfigurationProperties, ITextQuery } from 'vs/workbench/services/search/common/search'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { SearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor'; + + +export type SearchConfiguration = { + query: string, + includes: string, + excludes: string, + contextLines: number, + wholeWord: boolean, + caseSensitive: boolean, + regexp: boolean, + useIgnores: boolean +}; + +let searchEditorInputInstances = 0; +export class SearchEditorInput extends EditorInput { + static readonly ID: string = 'workbench.editorinputs.searchEditorInput'; + + public config: SearchConfiguration; + private instanceNumber: number = searchEditorInputInstances++; + + constructor( + config: SearchConfiguration | undefined, + @IModelService private readonly modelService: IModelService, + @IModeService private readonly modeService: IModeService, + ) { + super(); + + if (config === undefined) { + this.config = { query: '', includes: '', excludes: '', contextLines: 0, wholeWord: false, caseSensitive: false, regexp: false, useIgnores: true }; + } else { + this.config = config; + } + + const searchResultMode = this.modeService.create('search-result'); + this.modelService.createModel('', searchResultMode, this.getResource()); + } + + getTypeId(): string { + return SearchEditorInput.ID; + } + + getResource(): URI { + return URI.from({ scheme: 'code-search', fragment: `${this.instanceNumber}` }); + } + + getName(): string { + return this.config.query ? localize('searchTitle.withQuery', "Search: {0}", this.config.query) : localize('searchTitle', "Search"); + } + + setConfig(config: SearchConfiguration) { + this.config = config; + this._onDidChangeLabel.fire(); + } + + async resolve() { + return null; + } + + dispose() { + this.modelService.destroyModel(this.getResource()); + super.dispose(); + } +} + +// 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 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).replace(/\r?\n?$/, ''); + + 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; +}; + +type SearchResultSerialization = { text: string[], matchRanges: Range[] }; + +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 context: { line: string, lineNumber: number }[] = []; + fileMatch.context.forEach((line, lineNumber) => context.push({ line, lineNumber })); + context.sort((a, b) => a.lineNumber - b.lineNumber); + + let lastLine: number | undefined = undefined; + + const seenLines = new Set(); + serializedMatches.forEach(match => { + if (!seenLines.has(match.line)) { + while (context.length && context[0].lineNumber < +match.lineNumber) { + const { line, lineNumber } = context.shift()!; + if (lastLine !== undefined && lineNumber !== lastLine + 1) { + text.push(''); + } + text.push(` ${lineNumber} ${line}`); + lastLine = lineNumber; + } + + targetLineNumberToOffset[match.lineNumber] = text.length; + seenLines.add(match.line); + text.push(match.line); + lastLine = +match.lineNumber; + } + + matchRanges.push(...match.ranges.map(translateRangeLines(targetLineNumberToOffset[match.lineNumber]))); + }); + + while (context.length) { + const { line, lineNumber } = context.shift()!; + text.push(` ${lineNumber} ${line}`); + } + + 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 }; +}; + +const contentPatternToSearchResultHeader = (pattern: ITextQuery | null, includes: string, excludes: string, contextLines: number): 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 || pattern.userDisabledExcludesAndIgnoreFiles) + && `# Flags: ${coalesce([ + pattern.contentPattern.isCaseSensitive && 'CaseSensitive', + pattern.contentPattern.isWordMatch && 'WordMatch', + pattern.contentPattern.isRegExp && 'RegExp', + pattern.userDisabledExcludesAndIgnoreFiles && 'IgnoreExcludeSettings' + ]).join(' ')}`, + includes ? `# Including: ${includes}` : undefined, + excludes ? `# Excluding: ${excludes}` : undefined, + contextLines ? `# ContextLines: ${contextLines}` : undefined, + '' + ]); +}; + + +type SearchHeader = { + pattern: string; + flags: { + regex: boolean; + wholeWord: boolean; + caseSensitive: boolean; + ignoreExcludes: boolean; + }; + includes: string; + excludes: string; + context: number | undefined; +}; + +const searchHeaderToContentPattern = (header: string[]): SearchHeader => { + const query: SearchHeader = { + pattern: '', + flags: { regex: false, caseSensitive: false, ignoreExcludes: false, wholeWord: false }, + includes: '', + excludes: '', + context: undefined + }; + + const unescapeNewlines = (str: string) => { + let out = ''; + for (let i = 0; i < str.length; i++) { + if (str[i] === '\\') { + i++; + const escaped = str[i]; + + if (escaped === 'n') { + out += '\n'; + } + else if (escaped === '\\') { + out += '\\'; + } + else { + throw Error(localize('invalidQueryStringError', "All backslashes in Query string must be escaped (\\\\)")); + } + } else { + out += str[i]; + } + } + return out; + }; + 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 'ContextLines': query.context = +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; +}; + +export const serializeSearchResultForEditor = (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, contextLines: number, labelFormatter: (x: URI) => string, includeHeader: boolean): SearchResultSerialization => { + const header = includeHeader ? contentPatternToSearchResultHeader(searchResult.query, rawIncludePattern, rawExcludePattern, contextLines) : []; + const allResults = + flattenSearchResultSerializations( + 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 (contextLines: number | undefined, editorService: IEditorService, instantiationService: IInstantiationService, contextService: IWorkspaceContextService, labelService: ILabelService, configurationService: IConfigurationService) => { + const editorWidget = editorService.activeTextEditorWidget; + if (!isCodeEditor(editorWidget)) { + return; + } + + const textModel = editorWidget.getModel(); + if (!textModel) { return; } + + const header = textModel.getValueInRange(new Range(1, 1, 5, 1), EndOfLinePreference.LF) + .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 + }; + + contextLines = contextLines ?? contentPattern.context ?? 0; + + 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 + }, + afterContext: contextLines, + beforeContext: contextLines, + 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, contentPattern.includes, contentPattern.excludes, contextLines, labelFormatter, false); + + textModel.setValue(results.text.join(lineDelimiter)); + textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); + }; + +export const openNewSearchEditor = + async (editorService: IEditorService, instantiationService: IInstantiationService) => { + await editorService.openEditor(instantiationService.createInstance(SearchEditorInput, undefined), { pinned: true }); + }; + +export const createEditorFromSearchResult = + async (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, labelService: ILabelService, editorService: IEditorService, instantiationService: IInstantiationService) => { + if (!searchResult.query) { + console.error('Expected searchResult.query to be defined. Got', searchResult); + return; + } + + 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, 0, labelFormatter, false); + const contents = results.text.join(lineDelimiter); + let possible = { + contents, + mode: 'search-result', + resource: URI.from({ scheme: network.Schemas.untitled, path: searchTerm }) + }; + + let id = 0; + + let existing = editorService.getOpened(possible); + while (existing) { + if (existing instanceof UntitledTextEditorInput) { + const model = await existing.resolve(); + const existingContents = model.textEditorModel.getValue(EndOfLinePreference.LF); + if (existingContents === contents) { + break; + } + } + possible.resource = possible.resource.with({ path: searchTerm + '-' + ++id }); + existing = editorService.getOpened(possible); + } + + const editor = await editorService.openEditor( + instantiationService.createInstance( + SearchEditorInput, + { + query: searchResult.query.contentPattern.pattern, + regexp: !!searchResult.query.contentPattern.isRegExp, + caseSensitive: !!searchResult.query.contentPattern.isCaseSensitive, + wholeWord: !!searchResult.query.contentPattern.isWordMatch, + includes: rawIncludePattern, + excludes: rawExcludePattern, + contextLines: 0, + useIgnores: !searchResult.query.userDisabledExcludesAndIgnoreFiles, + }), + { pinned: true }) as SearchEditor; + + const model = assertIsDefined(editor.getModel()); + model.setValue(contents); + model.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); + }; + +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; }`); + } +}); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 16ba94358ca..1a7005618f5 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -64,7 +64,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { MultiCursorSelectionController } from 'vs/editor/contrib/multicursor/multicursor'; import { Selection } from 'vs/editor/common/core/selection'; import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; -import { createEditorFromSearchResult } from 'vs/workbench/contrib/search/browser/searchEditor'; +import { createEditorFromSearchResult } from 'vs/workbench/contrib/search/browser/searchEditorCommands'; import { ILabelService } from 'vs/platform/label/common/label'; import { Color, RGBA } from 'vs/base/common/color'; From d2911afe36d9ebe8d70b401d74f3fdec6dfd9988 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 10 Jan 2020 11:28:24 -0800 Subject: [PATCH 149/843] Move handler into CustomEditorInfoStore --- .../customEditor/browser/customEditors.ts | 70 ++++++++++--------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 908c38979fc..0336f929f0c 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -30,6 +30,7 @@ import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { CustomFileEditorInput } from './customEditorInput'; +import { Emitter } from 'vs/base/common/event'; export const defaultEditorId = 'default'; const defaultEditorInfo = new CustomEditorInfo({ @@ -41,37 +42,57 @@ const defaultEditorInfo = new CustomEditorInfo({ priority: CustomEditorPriority.default, }); -export class CustomEditorInfoStore { - private readonly contributedEditors = new Map(); +export class CustomEditorInfoStore extends Disposable { - public clear() { - this.contributedEditors.clear(); + private readonly _contributedEditors = new Map(); + + constructor() { + super(); + + webviewEditorsExtensionPoint.setHandler(extensions => { + this._contributedEditors.clear(); + + for (const extension of extensions) { + for (const webviewEditorContribution of extension.value) { + this.add(new CustomEditorInfo({ + id: webviewEditorContribution.viewType, + displayName: webviewEditorContribution.displayName, + selector: webviewEditorContribution.selector || [], + priority: webviewEditorContribution.priority || CustomEditorPriority.default, + })); + } + } + this._onChange.fire(); + }); } + private readonly _onChange = this._register(new Emitter()); + public readonly onChange = this._onChange.event; + public get(viewType: string): CustomEditorInfo | undefined { return viewType === defaultEditorId ? defaultEditorInfo - : this.contributedEditors.get(viewType); - } - - public add(info: CustomEditorInfo): void { - if (info.id === defaultEditorId || this.contributedEditors.has(info.id)) { - console.log(`Custom editor with id '${info.id}' already registered`); - return; - } - this.contributedEditors.set(info.id, info); + : this._contributedEditors.get(viewType); } public getContributedEditors(resource: URI): readonly CustomEditorInfo[] { - return Array.from(this.contributedEditors.values()).filter(customEditor => - customEditor.matches(resource)); + return Array.from(this._contributedEditors.values()) + .filter(customEditor => customEditor.matches(resource)); + } + + private add(info: CustomEditorInfo): void { + if (info.id === defaultEditorId || this._contributedEditors.has(info.id)) { + console.error(`Custom editor with id '${info.id}' already registered`); + return; + } + this._contributedEditors.set(info.id, info); } } export class CustomEditorService extends Disposable implements ICustomEditorService { _serviceBrand: any; - private readonly _editorInfoStore = new CustomEditorInfoStore(); + private readonly _editorInfoStore = this._register(new CustomEditorInfoStore()); private readonly _models: CustomEditorModelManager; @@ -94,26 +115,11 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ this._models = new CustomEditorModelManager(workingCopyService); - webviewEditorsExtensionPoint.setHandler(extensions => { - this._editorInfoStore.clear(); - - for (const extension of extensions) { - for (const webviewEditorContribution of extension.value) { - this._editorInfoStore.add(new CustomEditorInfo({ - id: webviewEditorContribution.viewType, - displayName: webviewEditorContribution.displayName, - selector: webviewEditorContribution.selector || [], - priority: webviewEditorContribution.priority || CustomEditorPriority.default, - })); - } - } - this.updateContexts(); - }); - this._hasCustomEditor = CONTEXT_HAS_CUSTOM_EDITORS.bindTo(contextKeyService); this._focusedCustomEditorIsEditable = CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE.bindTo(contextKeyService); this._webviewHasOwnEditFunctions = webviewHasOwnEditFunctionsContext.bindTo(contextKeyService); + this._register(this._editorInfoStore.onChange(() => this.updateContexts())); this._register(this.editorService.onDidActiveEditorChange(() => this.updateContexts())); this._register(fileService.onAfterOperation(e => { From 1b70625f8c7c3b8ae24e22c25d261c19f99746e0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 10 Jan 2020 11:50:45 -0800 Subject: [PATCH 150/843] Use import type when importing `vscode` under src When the core references `vscode`, we only want to import the types and never generate a real import (which will fail to load). Use `import type` to better enforce this --- src/vs/workbench/api/common/apiCommands.ts | 2 +- src/vs/workbench/api/common/extHostApiCommands.ts | 2 +- src/vs/workbench/api/common/extHostClipboard.ts | 2 +- src/vs/workbench/api/common/extHostCodeInsets.ts | 2 +- src/vs/workbench/api/common/extHostCommands.ts | 2 +- src/vs/workbench/api/common/extHostComments.ts | 2 +- src/vs/workbench/api/common/extHostConfiguration.ts | 2 +- src/vs/workbench/api/common/extHostDebugService.ts | 2 +- src/vs/workbench/api/common/extHostDecorations.ts | 2 +- src/vs/workbench/api/common/extHostDiagnostics.ts | 2 +- src/vs/workbench/api/common/extHostDialogs.ts | 2 +- src/vs/workbench/api/common/extHostDocumentContentProviders.ts | 2 +- src/vs/workbench/api/common/extHostDocumentData.ts | 2 +- src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts | 2 +- src/vs/workbench/api/common/extHostDocuments.ts | 2 +- src/vs/workbench/api/common/extHostExtensionActivator.ts | 2 +- src/vs/workbench/api/common/extHostExtensionService.ts | 2 +- src/vs/workbench/api/common/extHostFileSystem.ts | 2 +- src/vs/workbench/api/common/extHostFileSystemEventService.ts | 2 +- src/vs/workbench/api/common/extHostLanguageFeatures.ts | 2 +- src/vs/workbench/api/common/extHostLanguages.ts | 2 +- src/vs/workbench/api/common/extHostMemento.ts | 2 +- src/vs/workbench/api/common/extHostMessageService.ts | 2 +- src/vs/workbench/api/common/extHostOutput.ts | 2 +- src/vs/workbench/api/common/extHostSCM.ts | 2 +- src/vs/workbench/api/common/extHostSearch.ts | 2 +- src/vs/workbench/api/common/extHostTask.ts | 2 +- src/vs/workbench/api/common/extHostTerminalService.ts | 2 +- src/vs/workbench/api/common/extHostTextEditor.ts | 2 +- src/vs/workbench/api/common/extHostTextEditors.ts | 2 +- src/vs/workbench/api/common/extHostTreeViews.ts | 2 +- src/vs/workbench/api/common/extHostTunnelService.ts | 2 +- src/vs/workbench/api/common/extHostTypeConverters.ts | 2 +- src/vs/workbench/api/common/extHostTypes.ts | 2 +- src/vs/workbench/api/common/extHostUrls.ts | 2 +- src/vs/workbench/api/common/extHostWebview.ts | 2 +- src/vs/workbench/api/common/extHostWorkspace.ts | 2 +- src/vs/workbench/api/common/shared/webview.ts | 2 +- src/vs/workbench/api/node/extHostDebugService.ts | 2 +- src/vs/workbench/api/node/extHostOutputService.ts | 2 +- src/vs/workbench/api/node/extHostSearch.ts | 2 +- src/vs/workbench/api/node/extHostTask.ts | 2 +- src/vs/workbench/api/node/extHostTerminalService.ts | 2 +- src/vs/workbench/api/node/extHostTunnelService.ts | 2 +- .../test/electron-browser/api/extHostApiCommands.test.ts | 2 +- .../electron-browser/api/extHostDocumentSaveParticipant.test.ts | 2 +- .../test/electron-browser/api/extHostLanguageFeatures.test.ts | 2 +- .../workbench/test/electron-browser/api/extHostSearch.test.ts | 2 +- .../workbench/test/electron-browser/api/extHostWebview.test.ts | 2 +- 49 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index 01581c2c7c2..df619faf25e 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { URI } from 'vs/base/common/uri'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands'; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 312e9d1b4a9..0435b0a59b6 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as types from 'vs/workbench/api/common/extHostTypes'; import { IRawColorInfo, IWorkspaceEditDto, ICallHierarchyItemDto, IIncomingCallDto, IOutgoingCallDto } from 'vs/workbench/api/common/extHost.protocol'; diff --git a/src/vs/workbench/api/common/extHostClipboard.ts b/src/vs/workbench/api/common/extHostClipboard.ts index a34125b22c8..2983f22bd5b 100644 --- a/src/vs/workbench/api/common/extHostClipboard.ts +++ b/src/vs/workbench/api/common/extHostClipboard.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IMainContext, MainContext, MainThreadClipboardShape } from 'vs/workbench/api/common/extHost.protocol'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; export class ExtHostClipboard implements vscode.Clipboard { diff --git a/src/vs/workbench/api/common/extHostCodeInsets.ts b/src/vs/workbench/api/common/extHostCodeInsets.ts index 769e4de2a7a..8eabb9d74ab 100644 --- a/src/vs/workbench/api/common/extHostCodeInsets.ts +++ b/src/vs/workbench/api/common/extHostCodeInsets.ts @@ -8,7 +8,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor'; import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ExtHostEditorInsetsShape, MainThreadEditorInsetsShape } from './extHost.protocol'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; import { generateUuid } from 'vs/base/common/uuid'; diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts index 67f9d79f2c8..73ac4858560 100644 --- a/src/vs/workbench/api/common/extHostCommands.ts +++ b/src/vs/workbench/api/common/extHostCommands.ts @@ -11,7 +11,7 @@ import { cloneAndChange } from 'vs/base/common/objects'; import { MainContext, MainThreadCommandsShape, ExtHostCommandsShape, ObjectIdentifier, ICommandDto } from './extHost.protocol'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import * as modes from 'vs/editor/common/modes'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; import { revive } from 'vs/base/common/marshalling'; import { Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/workbench/api/common/extHostComments.ts b/src/vs/workbench/api/common/extHostComments.ts index 20e85761638..e090c8d0736 100644 --- a/src/vs/workbench/api/common/extHostComments.ts +++ b/src/vs/workbench/api/common/extHostComments.ts @@ -15,7 +15,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import * as extHostTypeConverter from 'vs/workbench/api/common/extHostTypeConverters'; import * as types from 'vs/workbench/api/common/extHostTypes'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShape, CommentThreadChanges } from './extHost.protocol'; import { ExtHostCommands } from './extHostCommands'; diff --git a/src/vs/workbench/api/common/extHostConfiguration.ts b/src/vs/workbench/api/common/extHostConfiguration.ts index ad9446b7394..7b1766de9a4 100644 --- a/src/vs/workbench/api/common/extHostConfiguration.ts +++ b/src/vs/workbench/api/common/extHostConfiguration.ts @@ -5,7 +5,7 @@ import { mixin, deepClone } from 'vs/base/common/objects'; import { Event, Emitter } from 'vs/base/common/event'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { ExtHostConfigurationShape, MainThreadConfigurationShape, IConfigurationInitData, MainContext } from './extHost.protocol'; import { ConfigurationTarget as ExtHostConfigurationTarget } from './extHostTypes'; diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index a3b0090c518..64eb6565b61 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -27,7 +27,7 @@ import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ISignService } from 'vs/platform/sign/common/sign'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { withNullAsUndefined } from 'vs/base/common/types'; diff --git a/src/vs/workbench/api/common/extHostDecorations.ts b/src/vs/workbench/api/common/extHostDecorations.ts index 1888766e31f..cccb18f5fa9 100644 --- a/src/vs/workbench/api/common/extHostDecorations.ts +++ b/src/vs/workbench/api/common/extHostDecorations.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { URI } from 'vs/base/common/uri'; import { MainContext, ExtHostDecorationsShape, MainThreadDecorationsShape, DecorationData, DecorationRequest, DecorationReply } from 'vs/workbench/api/common/extHost.protocol'; import { Disposable, Decoration } from 'vs/workbench/api/common/extHostTypes'; diff --git a/src/vs/workbench/api/common/extHostDiagnostics.ts b/src/vs/workbench/api/common/extHostDiagnostics.ts index e0b6831c552..9e06e6a7589 100644 --- a/src/vs/workbench/api/common/extHostDiagnostics.ts +++ b/src/vs/workbench/api/common/extHostDiagnostics.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { URI, UriComponents } from 'vs/base/common/uri'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol'; import { DiagnosticSeverity } from './extHostTypes'; import * as converter from './extHostTypeConverters'; diff --git a/src/vs/workbench/api/common/extHostDialogs.ts b/src/vs/workbench/api/common/extHostDialogs.ts index 06fcd380275..393db6ff77a 100644 --- a/src/vs/workbench/api/common/extHostDialogs.ts +++ b/src/vs/workbench/api/common/extHostDialogs.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { URI } from 'vs/base/common/uri'; import { MainContext, MainThreadDiaglogsShape, IMainContext } from 'vs/workbench/api/common/extHost.protocol'; diff --git a/src/vs/workbench/api/common/extHostDocumentContentProviders.ts b/src/vs/workbench/api/common/extHostDocumentContentProviders.ts index 56fda7f3a90..eba5e784576 100644 --- a/src/vs/workbench/api/common/extHostDocumentContentProviders.ts +++ b/src/vs/workbench/api/common/extHostDocumentContentProviders.ts @@ -7,7 +7,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/workbench/api/common/extHostTypes'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { MainContext, ExtHostDocumentContentProvidersShape, MainThreadDocumentContentProvidersShape, IMainContext } from './extHost.protocol'; import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors'; import { Schemas } from 'vs/base/common/network'; diff --git a/src/vs/workbench/api/common/extHostDocumentData.ts b/src/vs/workbench/api/common/extHostDocumentData.ts index 1ff19e1998c..cd871a8b382 100644 --- a/src/vs/workbench/api/common/extHostDocumentData.ts +++ b/src/vs/workbench/api/common/extHostDocumentData.ts @@ -11,7 +11,7 @@ import { MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; import { ensureValidWordDefinition, getWordAtText } from 'vs/editor/common/model/wordHelper'; import { MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol'; import { EndOfLine, Position, Range } from 'vs/workbench/api/common/extHostTypes'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { equals } from 'vs/base/common/arrays'; const _modeId2WordDefinition = new Map(); diff --git a/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts index 63f885cc69a..05c8e4bb11b 100644 --- a/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts @@ -12,7 +12,7 @@ import { TextEdit } from 'vs/workbench/api/common/extHostTypes'; import { Range, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/common/extHostTypeConverters'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { SaveReason } from 'vs/workbench/common/editor'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { LinkedList } from 'vs/base/common/linkedList'; import { ILogService } from 'vs/platform/log/common/log'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; diff --git a/src/vs/workbench/api/common/extHostDocuments.ts b/src/vs/workbench/api/common/extHostDocuments.ts index 60a0e162e36..d4406d5166a 100644 --- a/src/vs/workbench/api/common/extHostDocuments.ts +++ b/src/vs/workbench/api/common/extHostDocuments.ts @@ -11,7 +11,7 @@ import { ExtHostDocumentsShape, IMainContext, MainContext, MainThreadDocumentsSh import { ExtHostDocumentData, setWordDefinitionFor } from 'vs/workbench/api/common/extHostDocumentData'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { assertIsDefined } from 'vs/base/common/types'; export class ExtHostDocuments implements ExtHostDocumentsShape { diff --git a/src/vs/workbench/api/common/extHostExtensionActivator.ts b/src/vs/workbench/api/common/extHostExtensionActivator.ts index efa750e4b65..3a076946edd 100644 --- a/src/vs/workbench/api/common/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/common/extHostExtensionActivator.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index b0cb8707782..4fe9d010a9f 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -20,7 +20,7 @@ import { ExtensionActivationError } from 'vs/workbench/services/extensions/commo import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { Schemas } from 'vs/base/common/network'; import { VSBuffer } from 'vs/base/common/buffer'; diff --git a/src/vs/workbench/api/common/extHostFileSystem.ts b/src/vs/workbench/api/common/extHostFileSystem.ts index 2e16c4f8bff..a721785d28c 100644 --- a/src/vs/workbench/api/common/extHostFileSystem.ts +++ b/src/vs/workbench/api/common/extHostFileSystem.ts @@ -5,7 +5,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystemShape, IFileChangeDto } from './extHost.protocol'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import * as files from 'vs/platform/files/common/files'; import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; import { FileChangeType, FileSystemError } from 'vs/workbench/api/common/extHostTypes'; diff --git a/src/vs/workbench/api/common/extHostFileSystemEventService.ts b/src/vs/workbench/api/common/extHostFileSystemEventService.ts index 39bc81f6f8c..0f93730c131 100644 --- a/src/vs/workbench/api/common/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/common/extHostFileSystemEventService.ts @@ -7,7 +7,7 @@ import { AsyncEmitter, Emitter, Event, IWaitUntil } from 'vs/base/common/event'; import { IRelativePattern, parse } from 'vs/base/common/glob'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, MainThreadTextEditorsShape, IResourceFileEditDto, IResourceTextEditDto } from './extHost.protocol'; import * as typeConverter from './extHostTypeConverters'; import { Disposable, WorkspaceEdit } from './extHostTypes'; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 737ed4be714..af9a04c90f3 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -5,7 +5,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { mixin } from 'vs/base/common/objects'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits } from 'vs/workbench/api/common/extHostTypes'; import { ISingleEditOperation } from 'vs/editor/common/model'; diff --git a/src/vs/workbench/api/common/extHostLanguages.ts b/src/vs/workbench/api/common/extHostLanguages.ts index c6c3cc0c6e1..1231fcb0469 100644 --- a/src/vs/workbench/api/common/extHostLanguages.ts +++ b/src/vs/workbench/api/common/extHostLanguages.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { MainContext, MainThreadLanguagesShape, IMainContext } from './extHost.protocol'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; export class ExtHostLanguages { diff --git a/src/vs/workbench/api/common/extHostMemento.ts b/src/vs/workbench/api/common/extHostMemento.ts index 103b8f6a785..c74a5cffe5f 100644 --- a/src/vs/workbench/api/common/extHostMemento.ts +++ b/src/vs/workbench/api/common/extHostMemento.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; diff --git a/src/vs/workbench/api/common/extHostMessageService.ts b/src/vs/workbench/api/common/extHostMessageService.ts index 31154ab1de9..fc383d97265 100644 --- a/src/vs/workbench/api/common/extHostMessageService.ts +++ b/src/vs/workbench/api/common/extHostMessageService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import Severity from 'vs/base/common/severity'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; diff --git a/src/vs/workbench/api/common/extHostOutput.ts b/src/vs/workbench/api/common/extHostOutput.ts index aeae1a0aef4..a8bc2ddd474 100644 --- a/src/vs/workbench/api/common/extHostOutput.ts +++ b/src/vs/workbench/api/common/extHostOutput.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { MainContext, MainThreadOutputServiceShape, ExtHostOutputServiceShape } from './extHost.protocol'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 3b2943ff281..e27ae573bdd 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -12,7 +12,7 @@ import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto } from './extHost.protocol'; import { sortedDiff, equals } from 'vs/base/common/arrays'; import { comparePaths } from 'vs/base/common/comparers'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ISplice } from 'vs/base/common/sequence'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; diff --git a/src/vs/workbench/api/common/extHostSearch.ts b/src/vs/workbench/api/common/extHostSearch.ts index adc7c29d6ec..7ed796455c5 100644 --- a/src/vs/workbench/api/common/extHostSearch.ts +++ b/src/vs/workbench/api/common/extHostSearch.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ExtHostSearchShape, MainThreadSearchShape, MainContext } from '../common/extHost.protocol'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { FileSearchManager } from 'vs/workbench/services/search/common/fileSearchManager'; diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index fc9f83e5d2c..106635b772c 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -12,7 +12,7 @@ import { MainContext, MainThreadTaskShape, ExtHostTaskShape } from 'vs/workbench import * as types from 'vs/workbench/api/common/extHostTypes'; import { IExtHostWorkspaceProvider, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import * as tasks from '../common/shared/tasks'; import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index e08d1f50b85..f5635b83d23 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { Event, Emitter } from 'vs/base/common/event'; import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; diff --git a/src/vs/workbench/api/common/extHostTextEditor.ts b/src/vs/workbench/api/common/extHostTextEditor.ts index f57af9da4ef..635a5c6c253 100644 --- a/src/vs/workbench/api/common/extHostTextEditor.ts +++ b/src/vs/workbench/api/common/extHostTextEditor.ts @@ -13,7 +13,7 @@ import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, MainT import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { EndOfLine, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/common/extHostTypes'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; export class TextEditorDecorationType implements vscode.TextEditorDecorationType { diff --git a/src/vs/workbench/api/common/extHostTextEditors.ts b/src/vs/workbench/api/common/extHostTextEditors.ts index d70ab97f9cf..4a61bf07536 100644 --- a/src/vs/workbench/api/common/extHostTextEditors.ts +++ b/src/vs/workbench/api/common/extHostTextEditors.ts @@ -10,7 +10,7 @@ import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocum import { ExtHostTextEditor, TextEditorDecorationType } from 'vs/workbench/api/common/extHostTextEditor'; import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { TextEditorSelectionChangeKind } from 'vs/workbench/api/common/extHostTypes'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; export class ExtHostEditors implements ExtHostEditorsShape { diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 5fa7306fdf9..a17ec4889ca 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { basename } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Emitter, Event } from 'vs/base/common/event'; diff --git a/src/vs/workbench/api/common/extHostTunnelService.ts b/src/vs/workbench/api/common/extHostTunnelService.ts index f3e81eab7bd..fe666a32b52 100644 --- a/src/vs/workbench/api/common/extHostTunnelService.ts +++ b/src/vs/workbench/api/common/extHostTunnelService.ts @@ -5,7 +5,7 @@ import { ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { RemoteTunnel, TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { IDisposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 90ee0b882cb..093a3219eb5 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -10,7 +10,7 @@ import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/common/editor'; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 5239da9499b..eeb89627d62 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -11,7 +11,7 @@ import { values } from 'vs/base/common/map'; import { startsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files'; import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { escapeCodicons } from 'vs/base/common/codicons'; diff --git a/src/vs/workbench/api/common/extHostUrls.ts b/src/vs/workbench/api/common/extHostUrls.ts index 6b484fe0dec..6a0487c578e 100644 --- a/src/vs/workbench/api/common/extHostUrls.ts +++ b/src/vs/workbench/api/common/extHostUrls.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { MainContext, IMainContext, ExtHostUrlsShape, MainThreadUrlsShape } from './extHost.protocol'; import { URI, UriComponents } from 'vs/base/common/uri'; import { toDisposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 724b670d340..7c3cefab334 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -14,7 +14,7 @@ import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewEditorCapabilities, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol'; import { Disposable as VSCodeDisposable } from './extHostTypes'; diff --git a/src/vs/workbench/api/common/extHostWorkspace.ts b/src/vs/workbench/api/common/extHostWorkspace.ts index 4b5c0c1301f..edf45abf307 100644 --- a/src/vs/workbench/api/common/extHostWorkspace.ts +++ b/src/vs/workbench/api/common/extHostWorkspace.ts @@ -25,7 +25,7 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { Range, RelativePattern } from 'vs/workbench/api/common/extHostTypes'; import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder'; import { IRawFileMatch2, resultIsMatch } from 'vs/workbench/services/search/common/search'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ExtHostWorkspaceShape, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol'; export interface IExtHostWorkspaceProvider { diff --git a/src/vs/workbench/api/common/shared/webview.ts b/src/vs/workbench/api/common/shared/webview.ts index 6d5a52928f8..851a56985db 100644 --- a/src/vs/workbench/api/common/shared/webview.ts +++ b/src/vs/workbench/api/common/shared/webview.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; export interface WebviewInitData { readonly isExtensionDevelopmentDebug: boolean; diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index a4c70ad11e0..ee2d2c45bb1 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import * as env from 'vs/base/common/platform'; import { DebugAdapterExecutable } from 'vs/workbench/api/common/extHostTypes'; import { ExecutableDebugAdapter, SocketDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter'; diff --git a/src/vs/workbench/api/node/extHostOutputService.ts b/src/vs/workbench/api/node/extHostOutputService.ts index 4e6d32c4967..c7da758e4cc 100644 --- a/src/vs/workbench/api/node/extHostOutputService.ts +++ b/src/vs/workbench/api/node/extHostOutputService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { MainThreadOutputServiceShape } from '../common/extHost.protocol'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { URI } from 'vs/base/common/uri'; import { join } from 'vs/base/common/path'; import { OutputAppender } from 'vs/workbench/services/output/node/outputAppender'; diff --git a/src/vs/workbench/api/node/extHostSearch.ts b/src/vs/workbench/api/node/extHostSearch.ts index 3082bb70625..497f971404b 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -11,7 +11,7 @@ import { IFileQuery, IRawFileQuery, ISearchCompleteStats, isSerializedFileMatch, import { SearchService } from 'vs/workbench/services/search/node/rawSearchService'; import { RipgrepSearchProvider } from 'vs/workbench/services/search/node/ripgrepSearchProvider'; import { OutputChannel } from 'vs/workbench/services/search/node/ripgrepSearchUtils'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index d6d9a1ee84c..bcca770e837 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -9,7 +9,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { win32 } from 'vs/base/node/processes'; import * as types from 'vs/workbench/api/common/extHostTypes'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import * as tasks from '../common/shared/tasks'; import * as Objects from 'vs/base/common/objects'; import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService'; diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index f318235a519..8d2f73a9263 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import product from 'vs/platform/product/common/product'; import * as os from 'os'; import { URI, UriComponents } from 'vs/base/common/uri'; diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index a5932990726..58e07babca1 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -5,7 +5,7 @@ import { MainThreadTunnelServiceShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { URI } from 'vs/base/common/uri'; 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 7b4f95e3aa4..06138aaba40 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -23,7 +23,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { MainContext, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import 'vs/workbench/contrib/search/browser/search.contribution'; import { NullLogService } from 'vs/platform/log/common/log'; diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts index a219765f48b..759da107345 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts @@ -11,7 +11,7 @@ import { MainThreadTextEditorsShape, IWorkspaceEditDto } from 'vs/workbench/api/ import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostDocumentSaveParticipant'; import { SingleProxyRPCProtocol } from './testRPCProtocol'; import { SaveReason } from 'vs/workbench/common/editor'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import { NullLogService } from 'vs/platform/log/common/log'; import { isResourceTextEdit, ResourceTextEdit } from 'vs/editor/common/modes'; diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index 9bb319fe7b7..232becb2a56 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -35,7 +35,7 @@ import { getDocumentFormattingEditsUntilResult, getDocumentRangeFormattingEditsU import { getLinks } from 'vs/editor/contrib/links/getLinks'; import { MainContext, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { NullLogService } from 'vs/platform/log/common/log'; import { ITextModel, EndOfLineSequence } from 'vs/editor/common/model'; diff --git a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts index d90cbe04256..ab0b20cdd86 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -16,7 +16,7 @@ import { NativeExtHostSearch } from 'vs/workbench/api/node/extHostSearch'; import { Range } from 'vs/workbench/api/common/extHostTypes'; import { IFileMatch, IFileQuery, IPatternInfo, IRawFileMatch2, ISearchCompleteStats, ISearchQuery, ITextQuery, QueryType, resultIsMatch } from 'vs/workbench/services/search/common/search'; import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { NullLogService } from 'vs/platform/log/common/log'; import { URITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; diff --git a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts index 8febbdbdf9e..3828c1f7811 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts @@ -11,7 +11,7 @@ import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview'; import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { SingleProxyRPCProtocol } from './testRPCProtocol'; suite('ExtHostWebview', () => { From 5c71a0b18b83b5fae09978770df7eb3813d9b076 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 10 Jan 2020 13:24:41 -0800 Subject: [PATCH 151/843] Update js/ts grammar --- .../syntaxes/JavaScript.tmLanguage.json | 63 ++++++++++++------- .../syntaxes/JavaScriptReact.tmLanguage.json | 63 ++++++++++++------- extensions/typescript-basics/cgmanifest.json | 2 +- .../syntaxes/TypeScript.tmLanguage.json | 63 ++++++++++++------- .../syntaxes/TypeScriptReact.tmLanguage.json | 63 ++++++++++++------- 5 files changed, 157 insertions(+), 97 deletions(-) diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 54169e925da..c18fb6cfef7 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/b7ef0941e38d56292d19ee7706a558dbff6c3a35", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/f065e7e88d1c20160c5ec92455aad99a1016284f", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -1081,13 +1081,13 @@ }, "field-declaration": { "name": "meta.field.declaration.js", - "begin": "(?x)(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.js entity.name.function.js" @@ -1123,7 +1123,7 @@ }, { "name": "meta.definition.property.js variable.object.property.js", - "match": "[_$[:alpha:]][_$[:alnum:]]*" + "match": "\\#?[_$[:alpha:]][_$[:alnum:]]*" }, { "name": "keyword.operator.optional.js", @@ -1998,7 +1998,7 @@ "patterns": [ { "name": "meta.import-equals.external.js", - "begin": "(?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { @@ -2834,7 +2849,7 @@ }, { "name": "entity.name.function.js", - "match": "([_$[:alpha:]][_$[:alnum:]]*)" + "match": "(\\#?[_$[:alpha:]][_$[:alnum:]]*)" } ] }, @@ -3626,7 +3641,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", "captures": { "1": { "name": "punctuation.accessor.js" @@ -3640,7 +3655,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.js" @@ -3670,7 +3685,7 @@ "match": "([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\\??\\.\\s*prototype\\b(?!\\$))" }, { - "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n (\\#?[[:upper:]][_$[:digit:][:upper:]]*) |\n (\\#?[_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.js" @@ -3687,7 +3702,7 @@ } }, { - "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "variable.other.constant.object.js" diff --git a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json index 9c5e4e15474..29cd81e4ab3 100644 --- a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/b7ef0941e38d56292d19ee7706a558dbff6c3a35", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/f065e7e88d1c20160c5ec92455aad99a1016284f", "name": "JavaScript (with React support)", "scopeName": "source.js.jsx", "patterns": [ @@ -1081,13 +1081,13 @@ }, "field-declaration": { "name": "meta.field.declaration.js.jsx", - "begin": "(?x)(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.js.jsx entity.name.function.js.jsx" @@ -1123,7 +1123,7 @@ }, { "name": "meta.definition.property.js.jsx variable.object.property.js.jsx", - "match": "[_$[:alpha:]][_$[:alnum:]]*" + "match": "\\#?[_$[:alpha:]][_$[:alnum:]]*" }, { "name": "keyword.operator.optional.js.jsx", @@ -1998,7 +1998,7 @@ "patterns": [ { "name": "meta.import-equals.external.js.jsx", - "begin": "(?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js.jsx", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { @@ -2834,7 +2849,7 @@ }, { "name": "entity.name.function.js.jsx", - "match": "([_$[:alpha:]][_$[:alnum:]]*)" + "match": "(\\#?[_$[:alpha:]][_$[:alnum:]]*)" } ] }, @@ -3626,7 +3641,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -3640,7 +3655,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -3670,7 +3685,7 @@ "match": "([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\\??\\.\\s*prototype\\b(?!\\$))" }, { - "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n (\\#?[[:upper:]][_$[:digit:][:upper:]]*) |\n (\\#?[_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -3687,7 +3702,7 @@ } }, { - "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "variable.other.constant.object.js.jsx" diff --git a/extensions/typescript-basics/cgmanifest.json b/extensions/typescript-basics/cgmanifest.json index 31f2e0c545a..4136543a84f 100644 --- a/extensions/typescript-basics/cgmanifest.json +++ b/extensions/typescript-basics/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "TypeScript-TmLanguage", "repositoryUrl": "https://github.com/Microsoft/TypeScript-TmLanguage", - "commitHash": "b7ef0941e38d56292d19ee7706a558dbff6c3a35" + "commitHash": "f065e7e88d1c20160c5ec92455aad99a1016284f" } }, "license": "MIT", diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index 437e205163f..4edc3b8285e 100644 --- a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/b7ef0941e38d56292d19ee7706a558dbff6c3a35", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/f065e7e88d1c20160c5ec92455aad99a1016284f", "name": "TypeScript", "scopeName": "source.ts", "patterns": [ @@ -1078,13 +1078,13 @@ }, "field-declaration": { "name": "meta.field.declaration.ts", - "begin": "(?x)(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.ts entity.name.function.ts" @@ -1120,7 +1120,7 @@ }, { "name": "meta.definition.property.ts variable.object.property.ts", - "match": "[_$[:alpha:]][_$[:alnum:]]*" + "match": "\\#?[_$[:alpha:]][_$[:alnum:]]*" }, { "name": "keyword.operator.optional.ts", @@ -1995,7 +1995,7 @@ "patterns": [ { "name": "meta.import-equals.external.ts", - "begin": "(?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { "name": "meta.function-call.ts", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { @@ -2831,7 +2846,7 @@ }, { "name": "entity.name.function.ts", - "match": "([_$[:alpha:]][_$[:alnum:]]*)" + "match": "(\\#?[_$[:alpha:]][_$[:alnum:]]*)" } ] }, @@ -3675,7 +3690,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -3689,7 +3704,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -3719,7 +3734,7 @@ "match": "([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\\??\\.\\s*prototype\\b(?!\\$))" }, { - "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n (\\#?[[:upper:]][_$[:digit:][:upper:]]*) |\n (\\#?[_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -3736,7 +3751,7 @@ } }, { - "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "variable.other.constant.object.ts" diff --git a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json index c2c6c09a5a2..810cac1b2db 100644 --- a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/b7ef0941e38d56292d19ee7706a558dbff6c3a35", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/f065e7e88d1c20160c5ec92455aad99a1016284f", "name": "TypeScriptReact", "scopeName": "source.tsx", "patterns": [ @@ -1081,13 +1081,13 @@ }, "field-declaration": { "name": "meta.field.declaration.tsx", - "begin": "(?x)(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.tsx entity.name.function.tsx" @@ -1123,7 +1123,7 @@ }, { "name": "meta.definition.property.tsx variable.object.property.tsx", - "match": "[_$[:alpha:]][_$[:alnum:]]*" + "match": "\\#?[_$[:alpha:]][_$[:alnum:]]*" }, { "name": "keyword.operator.optional.tsx", @@ -1998,7 +1998,7 @@ "patterns": [ { "name": "meta.import-equals.external.tsx", - "begin": "(?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { "name": "meta.function-call.tsx", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*)(\\s*\\??\\.\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*))*)|(\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*))", "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof|readonly)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { @@ -2834,7 +2849,7 @@ }, { "name": "entity.name.function.tsx", - "match": "([_$[:alpha:]][_$[:alnum:]]*)" + "match": "(\\#?[_$[:alpha:]][_$[:alnum:]]*)" } ] }, @@ -3626,7 +3641,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -3640,7 +3655,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -3670,7 +3685,7 @@ "match": "([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\\??\\.\\s*prototype\\b(?!\\$))" }, { - "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n (\\#?[[:upper:]][_$[:digit:][:upper:]]*) |\n (\\#?[_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -3687,7 +3702,7 @@ } }, { - "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "variable.other.constant.object.tsx" From a0328d26aca420f9d0606b825f5a326dc6d0d398 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 10 Jan 2020 14:24:50 -0800 Subject: [PATCH 152/843] Add 'vscode.reopenWith' API command Fixes #88426 Add an api command that allows extensions to open/reopen a file with a specific we custom editor. Use this to allow re-opening a failed to load image as text/binary For #77131 --- extensions/image-preview/media/main.css | 6 +-- extensions/image-preview/media/main.js | 6 +++ extensions/image-preview/src/preview.ts | 11 ++++- src/vs/workbench/api/common/apiCommands.ts | 18 +++++++++ .../contrib/customEditor/browser/commands.ts | 40 +++++-------------- 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/extensions/image-preview/media/main.css b/extensions/image-preview/media/main.css index ff3e6d0f0d2..49a01b8d969 100644 --- a/extensions/image-preview/media/main.css +++ b/extensions/image-preview/media/main.css @@ -94,16 +94,16 @@ body img { } .loading-indicator, -.image-load-error-message { +.image-load-error { display: none; } .loading .loading-indicator, -.error .image-load-error-message { +.error .image-load-error { display: block; } -.image-load-error-message { +.image-load-error { margin: 1em; } diff --git a/extensions/image-preview/media/main.js b/extensions/image-preview/media/main.js index 3e2abf4ee98..394511aa8ef 100644 --- a/extensions/image-preview/media/main.js +++ b/extensions/image-preview/media/main.js @@ -305,6 +305,12 @@ image.src = settings.src; + document.querySelector('.open-file-link').addEventListener('click', () => { + vscode.postMessage({ + type: 'reopen-as-text', + }); + }); + window.addEventListener('message', e => { switch (e.data.type) { case 'setScale': diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index dc2e6676245..4c21b052509 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -108,6 +108,12 @@ class Preview extends Disposable { this.update(); break; } + + case 'reopen-as-text': + { + vscode.commands.executeCommand('vscode.openWith', resource, 'default', webviewEditor.viewColumn); + break; + } } })); @@ -218,7 +224,10 @@ class Preview extends Disposable {
-
${localize('preview.imageLoadError', "An error occurred while loading the image")}
+
+

${localize('preview.imageLoadError', "An error occurred while loading the image.")}

+ ${localize('preview.imageLoadErrorLink', "Open file using VS Code's standard text/binary editor?")} +
`; diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index df619faf25e..d2c28563d48 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -132,6 +132,24 @@ export class OpenAPICommand { } CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute)); +export class OpenWithAPICommand { + public static readonly ID = 'vscode.openWith'; + public static execute(executor: ICommandsExecutor, resource: URI, viewType: string, column?: vscode.ViewColumn): Promise { + let position: EditorViewColumn | undefined; + + if (typeof column === 'number') { + position = typeConverters.ViewColumn.from(column); + } + + return executor.executeCommand('_workbench.openWith', [ + resource, + viewType, + position + ]); + } +} +CommandsRegistry.registerCommand(OpenWithAPICommand.ID, adjustHandler(OpenWithAPICommand.execute)); + CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, uri: URI) { const workspacesService = accessor.get(IWorkspacesService); return workspacesService.removeFromRecentlyOpened([uri]); diff --git a/src/vs/workbench/contrib/customEditor/browser/commands.ts b/src/vs/workbench/contrib/customEditor/browser/commands.ts index 1022aa8e3f8..5db57f251a4 100644 --- a/src/vs/workbench/contrib/customEditor/browser/commands.ts +++ b/src/vs/workbench/contrib/customEditor/browser/commands.ts @@ -3,59 +3,37 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { firstOrDefault } from 'vs/base/common/arrays'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { URI } from 'vs/base/common/uri'; import { Command } from 'vs/editor/browser/editorExtensions'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import * as nls from 'vs/nls'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IListService } from 'vs/platform/list/browser/listService'; +import { EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorCommandsContext } from 'vs/workbench/common/editor'; +import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { defaultEditorId } from 'vs/workbench/contrib/customEditor/browser/customEditors'; import { CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CONTEXT_HAS_CUSTOM_EDITORS, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; -import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; -import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; const viewCategory = nls.localize('viewCategory', "View"); // #region Open With -const OPEN_WITH_COMMAND_ID = 'openWith'; -// const OPEN_WITH_TITLE = { value: nls.localize('openWith.title', 'Open With'), original: 'Open With' }; +CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAccessor, args: [URI, string, EditorViewColumn]) => { + const customEditorService = accessor.get(ICustomEditorService); + const editorGroupService = accessor.get(IEditorGroupsService); -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: OPEN_WITH_COMMAND_ID, - weight: KeybindingWeight.WorkbenchContrib, - when: EditorContextKeys.focus.toNegated(), - handler: async (accessor: ServicesAccessor, resource: URI | object) => { - const editorService = accessor.get(IEditorService); - const resources = getMultiSelectedResources(resource, accessor.get(IListService), editorService, accessor.get(IExplorerService)); - const targetResource = firstOrDefault(resources); - if (!targetResource) { - return; - } - return accessor.get(ICustomEditorService).promptOpenWith(targetResource, undefined, undefined); - } + const [resource, viewType, position] = args; + const group = viewColumnToEditorGroup(editorGroupService, position); + customEditorService.openWith(resource, viewType, undefined, editorGroupService.getGroup(group)); }); -// MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { -// group: 'navigation', -// order: 20, -// command: { -// id: OPEN_WITH_COMMAND_ID, -// title: OPEN_WITH_TITLE, -// }, -// when: ResourceContextKey.Scheme.isEqualTo(Schemas.file) -// }); - // #endregion // #region Reopen With From f6f9931377b864e5f1a6ca8081921f726508ca75 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 10 Jan 2020 14:34:38 -0800 Subject: [PATCH 153/843] add launch.json settings to make js-debug faster --- .vscode/launch.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 6fa93e1c9c5..15dcd01344e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -162,9 +162,12 @@ "urlFilter": "*workbench.html*", "runtimeArgs": [ "--inspect=5875", - "--no-cached-data" + "--no-cached-data", ], - "webRoot": "${workspaceFolder}" + "webRoot": "${workspaceFolder}", + // Settings for js-debug: + "pauseForSourceMap": false, + "outFiles": ["${workspaceFolder}/out/**/*.js"], }, { "type": "node", From 59213a4c2797e532c59bb9b1463684b688e34fb2 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 10 Jan 2020 16:20:59 -0800 Subject: [PATCH 154/843] Keep track of query includes/excludes expansion state --- .../contrib/search/browser/searchEditor.ts | 21 ++++++++++++------- .../search/browser/searchEditorCommands.ts | 6 ++++-- .../contrib/search/browser/searchWidget.ts | 5 ++--- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchEditor.ts b/src/vs/workbench/contrib/search/browser/searchEditor.ts index 8e7e247f519..505ea541890 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditor.ts @@ -57,6 +57,7 @@ export class SearchEditor extends BaseEditor { private runSearchDelayer = new Delayer(300); private pauseSearching: boolean = false; + private showingIncludesExcludes: boolean = false; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -82,7 +83,7 @@ export class SearchEditor extends BaseEditor { this.queryEditorWidget = this._register(this.instantiationService.createInstance(SearchWidget, this.queryEditorContainer, { _hideReplaceToggle: true, showContextToggle: true })); this._register(this.queryEditorWidget.onReplaceToggled(() => this.reLayout())); this._register(this.queryEditorWidget.onDidHeightChange(() => this.reLayout())); - this.queryEditorWidget.onSearchSubmit(() => this.runSearch()); + this.queryEditorWidget.onSearchSubmit(() => this.runSearch(true)); // onSearchSubmit has an internal delayer, so skip over ours. this.queryEditorWidget.searchInput.onDidOptionChange(() => this.runSearch()); this.queryEditorWidget.onDidToggleContext(() => this.runSearch()); @@ -92,14 +93,14 @@ export class SearchEditor extends BaseEditor { this.toggleQueryDetailsButton = DOM.append(this.includesExcludesContainer, DOM.$('.expand.codicon.codicon-ellipsis', { tabindex: 0, role: 'button', title: localize('moreSearch', "Toggle Search Details") })); this._register(DOM.addDisposableListener(this.toggleQueryDetailsButton, DOM.EventType.CLICK, e => { DOM.EventHelper.stop(e); - this.toggleQueryDetails(); + this.toggleIncludesExcludes(); })); this._register(DOM.addDisposableListener(this.toggleQueryDetailsButton, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { const event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { DOM.EventHelper.stop(e); - this.toggleQueryDetails(); + this.toggleIncludesExcludes(); } })); this._register(DOM.addDisposableListener(this.toggleQueryDetailsButton, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { @@ -159,9 +160,9 @@ export class SearchEditor extends BaseEditor { }); } - private async runSearch() { + private async runSearch(instant = false) { if (!this.pauseSearching) { - this.runSearchDelayer.trigger(() => this.doRunSearch()); + this.runSearchDelayer.trigger(() => this.doRunSearch(), instant ? 0 : undefined); } } @@ -176,7 +177,8 @@ export class SearchEditor extends BaseEditor { query: this.queryEditorWidget.searchInput.getValue(), regexp: this.queryEditorWidget.searchInput.getRegex(), wholeWord: this.queryEditorWidget.searchInput.getWholeWords(), - useIgnores: this.inputPatternExcludes.useExcludesAndIgnoreFiles() + useIgnores: this.inputPatternExcludes.useExcludesAndIgnoreFiles(), + showIncludesExcludes: this.showingIncludesExcludes }; const content: IPatternInfo = { @@ -264,15 +266,16 @@ export class SearchEditor extends BaseEditor { this.inputPatternExcludes.setValue(newInput.config.excludes); this.inputPatternIncludes.setValue(newInput.config.includes); this.inputPatternExcludes.setUseExcludesAndIgnoreFiles(newInput.config.useIgnores); + this.toggleIncludesExcludes(newInput.config.showIncludesExcludes); this.focusInput(); await super.setInput(newInput, options, token); this.pauseSearching = false; } - toggleQueryDetails(): void { + private toggleIncludesExcludes(_shouldShow?: boolean): void { const cls = 'expanded'; - const shouldShow = !DOM.hasClass(this.includesExcludesContainer, cls); + const shouldShow = _shouldShow ?? !DOM.hasClass(this.includesExcludesContainer, cls); if (shouldShow) { this.toggleQueryDetailsButton.setAttribute('aria-expanded', 'true'); @@ -282,6 +285,8 @@ export class SearchEditor extends BaseEditor { DOM.removeClass(this.includesExcludesContainer, cls); } + this.showingIncludesExcludes = DOM.hasClass(this.includesExcludesContainer, cls); + this.reLayout(); } diff --git a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts index d6735a62c12..8156667cbaa 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts @@ -39,7 +39,8 @@ export type SearchConfiguration = { wholeWord: boolean, caseSensitive: boolean, regexp: boolean, - useIgnores: boolean + useIgnores: boolean, + showIncludesExcludes: boolean, }; let searchEditorInputInstances = 0; @@ -57,7 +58,7 @@ export class SearchEditorInput extends EditorInput { super(); if (config === undefined) { - this.config = { query: '', includes: '', excludes: '', contextLines: 0, wholeWord: false, caseSensitive: false, regexp: false, useIgnores: true }; + this.config = { query: '', includes: '', excludes: '', contextLines: 0, wholeWord: false, caseSensitive: false, regexp: false, useIgnores: true, showIncludesExcludes: false }; } else { this.config = config; } @@ -416,6 +417,7 @@ export const createEditorFromSearchResult = excludes: rawExcludePattern, contextLines: 0, useIgnores: !searchResult.query.userDisabledExcludesAndIgnoreFiles, + showIncludesExcludes: !!(rawExcludePattern || rawExcludePattern || searchResult.query.userDisabledExcludesAndIgnoreFiles) }), { pinned: true }) as SearchEditor; diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 402f4313f1a..95f74747c93 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -160,7 +160,6 @@ export class SearchWidget extends Widget { private temporarilySkipSearchOnChange = false; private showContextCheckbox!: Checkbox; private contextLinesInput!: InputBox; - private _contextLineInputDelayer: Delayer; constructor( container: HTMLElement, @@ -179,7 +178,6 @@ export class SearchWidget extends Widget { this.replaceInputBoxFocused = Constants.ReplaceInputBoxFocusedKey.bindTo(this.contextKeyService); this._replaceHistoryDelayer = new Delayer(500); - this._contextLineInputDelayer = new Delayer(300); this._searchDelayer = this._register(new Delayer(this.searchConfiguration.searchOnTypeDebouncePeriod)); this.render(container, options); @@ -386,7 +384,7 @@ export class SearchWidget extends Widget { if (this.contextLinesInput.value.includes('-')) { this.contextLinesInput.value = '0'; } - this._contextLineInputDelayer.trigger(() => this._onDidToggleContext.fire()); + this._onDidToggleContext.fire(); })); dom.append(searchInputContainer, this.showContextCheckbox.domNode); } @@ -400,6 +398,7 @@ export class SearchWidget extends Widget { this.showContextCheckbox.checked = true; this.contextLinesInput.value = '' + lines; } + dom.toggleClass(this.domNode, 'show-context', this.showContextCheckbox.checked); } private renderReplaceInput(parent: HTMLElement, options: ISearchWidgetOptions): void { From b9ad9c968a0786a2d13faf709903226e27e9c840 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 10 Jan 2020 17:56:28 -0800 Subject: [PATCH 155/843] Restore search editor configurations on reload --- .../search/browser/search.contribution.ts | 7 ++++++- .../search/browser/searchEditorCommands.ts | 17 +++++++++++++++-- .../contrib/search/browser/searchWidget.ts | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 2e499c9e06d..3a3383efb8e 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -57,9 +57,10 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { assertType } from 'vs/base/common/types'; import { SearchViewPaneContainer } from 'vs/workbench/contrib/search/browser/searchViewlet'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; -import { SearchEditorInput } from 'vs/workbench/contrib/search/browser/searchEditorCommands'; +import { SearchEditorInput, SearchEditorInputFactory } from 'vs/workbench/contrib/search/browser/searchEditorCommands'; import { SearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); registerSingleton(ISearchHistoryService, SearchHistoryService, true); @@ -904,3 +905,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( new SyncDescriptor(SearchEditorInput) ] ); + +Registry.as(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory( + SearchEditorInput.ID, + SearchEditorInputFactory); diff --git a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts index 8156667cbaa..6d9e6ee65ee 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts @@ -25,7 +25,7 @@ import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/co import { FileMatch, Match, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPatternInfo, ISearchConfigurationProperties, ITextQuery } from 'vs/workbench/services/search/common/search'; -import { EditorInput } from 'vs/workbench/common/editor'; +import { EditorInput, IEditorInputFactory } from 'vs/workbench/common/editor'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { SearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor'; @@ -43,6 +43,19 @@ export type SearchConfiguration = { showIncludesExcludes: boolean, }; +export class SearchEditorInputFactory implements IEditorInputFactory { + + canSerialize() { return true; } + + serialize(input: SearchEditorInput) { + return JSON.stringify(input.config); + } + + deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): SearchEditorInput | undefined { + return instantiationService.createInstance(SearchEditorInput, JSON.parse(serializedEditorInput)); + } +} + let searchEditorInputInstances = 0; export class SearchEditorInput extends EditorInput { static readonly ID: string = 'workbench.editorinputs.searchEditorInput'; @@ -72,7 +85,7 @@ export class SearchEditorInput extends EditorInput { } getResource(): URI { - return URI.from({ scheme: 'code-search', fragment: `${this.instanceNumber}` }); + return URI.from({ scheme: 'untitled', authority: 'search-editor', path: this.config.query, fragment: `${this.instanceNumber}` }); } getName(): string { diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 95f74747c93..30b5c34eb4a 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -452,8 +452,8 @@ export class SearchWidget extends Widget { } setValue(value: string, skipSearchOnChange: boolean) { - this.searchInput.setValue(value); this.temporarilySkipSearchOnChange = skipSearchOnChange || this.temporarilySkipSearchOnChange; + this.searchInput.setValue(value); } setReplaceAllActionState(enabled: boolean): void { From 4fb03413db8e1df7bae16eeb2d6604579167bcc7 Mon Sep 17 00:00:00 2001 From: Fabien Launay Date: Sat, 11 Jan 2020 11:08:15 +0900 Subject: [PATCH 156/843] Fix word repetition in lazy.test.ts comment (#88464) --- src/vs/base/test/common/lazy.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/test/common/lazy.test.ts b/src/vs/base/test/common/lazy.test.ts index c6a1655513f..04d4a256985 100644 --- a/src/vs/base/test/common/lazy.test.ts +++ b/src/vs/base/test/common/lazy.test.ts @@ -47,7 +47,7 @@ suite('Lazy', () => { assert.deepEqual(innerLazy.getValue(), [1, 11]); }); - test('map should should handle error values', () => { + test('map should handle error values', () => { let outer = 0; let inner = 10; const outerLazy = new Lazy(() => { throw new Error(`${++outer}`); }); From cb4ef36e6d489f74a1346fa89cdabe727bcd6027 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 10 Jan 2020 16:24:55 -0800 Subject: [PATCH 157/843] Make sure we restore the previous editor when switching back to it --- src/vs/workbench/api/common/apiCommands.ts | 10 +++-- .../contrib/customEditor/browser/commands.ts | 7 ++-- .../customEditor/browser/customEditors.ts | 39 ++++++++++++------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index d2c28563d48..69fc23a9af1 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -134,16 +134,20 @@ CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand export class OpenWithAPICommand { public static readonly ID = 'vscode.openWith'; - public static execute(executor: ICommandsExecutor, resource: URI, viewType: string, column?: vscode.ViewColumn): Promise { + public static execute(executor: ICommandsExecutor, resource: URI, viewType: string, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions): Promise { + let options: ITextEditorOptions | undefined; let position: EditorViewColumn | undefined; - if (typeof column === 'number') { - position = typeConverters.ViewColumn.from(column); + if (typeof columnOrOptions === 'number') { + position = typeConverters.ViewColumn.from(columnOrOptions); + } else if (typeof columnOrOptions !== 'undefined') { + options = typeConverters.TextEditorOptions.from(columnOrOptions); } return executor.executeCommand('_workbench.openWith', [ resource, viewType, + options, position ]); } diff --git a/src/vs/workbench/contrib/customEditor/browser/commands.ts b/src/vs/workbench/contrib/customEditor/browser/commands.ts index 5db57f251a4..6814383e70b 100644 --- a/src/vs/workbench/contrib/customEditor/browser/commands.ts +++ b/src/vs/workbench/contrib/customEditor/browser/commands.ts @@ -20,18 +20,19 @@ import { defaultEditorId } from 'vs/workbench/contrib/customEditor/browser/custo import { CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CONTEXT_HAS_CUSTOM_EDITORS, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import type { ITextEditorOptions } from 'vs/platform/editor/common/editor'; const viewCategory = nls.localize('viewCategory', "View"); // #region Open With -CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAccessor, args: [URI, string, EditorViewColumn]) => { +CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAccessor, args: [URI, string, ITextEditorOptions | undefined, EditorViewColumn | undefined]) => { const customEditorService = accessor.get(ICustomEditorService); const editorGroupService = accessor.get(IEditorGroupsService); - const [resource, viewType, position] = args; + const [resource, viewType, options, position] = args; const group = viewColumnToEditorGroup(editorGroupService, position); - customEditorService.openWith(resource, viewType, undefined, editorGroupService.getGroup(group)); + customEditorService.openWith(resource, viewType, options, editorGroupService.getGroup(group)); }); // #endregion diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 0336f929f0c..25ce3d2fc38 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -240,23 +240,25 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ options?: IEditorOptions, group?: IEditorGroup ): Promise { - if (group) { - const existingEditors = group.editors.filter(editor => editor.getResource() && isEqual(editor.getResource(), resource)); - if (existingEditors.length) { - const existing = existingEditors[0]; - if (!input.matches(existing)) { - await this.editorService.replaceEditors([{ - editor: existing, - replacement: input, - options: options ? EditorOptions.create(options) : undefined, - }], group); + const targetGroup = group || this.editorGroupService.activeGroup; - if (existing instanceof CustomFileEditorInput) { - existing.dispose(); - } + // Try to replace existing editors for resource + const existingEditors = targetGroup.editors.filter(editor => editor.getResource() && isEqual(editor.getResource(), resource)); + if (existingEditors.length) { + const existing = existingEditors[0]; + if (!input.matches(existing)) { + await this.editorService.replaceEditors([{ + editor: existing, + replacement: input, + options: options ? EditorOptions.create(options) : undefined, + }], targetGroup); + + if (existing instanceof CustomFileEditorInput) { + existing.dispose(); } } } + return this.editorService.openEditor(input, options, group); } @@ -345,6 +347,17 @@ export class CustomEditorContribution implements IWorkbenchContribution { options: ITextEditorOptions | undefined, group: IEditorGroup ): IOpenEditorOverride | undefined { + // Check to see if there already an editor for the resource in the group. + // If there is, we want to open that instead of creating a new editor. + // This ensures that we preserve whatever state the editor was previously in + // when the user switches back to it. + const existingEditorForResource = group.editors.find(editor => isEqual(resource, editor.getResource())); + if (existingEditorForResource) { + return { + override: this.editorService.openEditor(existingEditorForResource, { ...options, ignoreOverrides: true }, group) + }; + } + const userConfiguredEditors = this.customEditorService.getUserConfiguredCustomEditors(resource); if (userConfiguredEditors.length) { return { From 8520f74c08cd24ee6525df7c930ea24eb2f7334c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 10 Jan 2020 18:17:31 -0800 Subject: [PATCH 158/843] Implement a default saveAs for readonly custom editors Fixes #88414 --- .../workbench/api/browser/mainThreadWebview.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index d0fe331c5f5..bac1b9b6ba2 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -12,6 +12,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import * as modes from 'vs/editor/common/modes'; import { localize } from 'vs/nls'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IFileService } from 'vs/platform/files/common/files'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -107,6 +108,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma @IProductService private readonly _productService: IProductService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, + @IFileService private readonly _fileService: IFileService, ) { super(); @@ -319,7 +321,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._customEditorModels.set(model, { referenceCount: 1 }); const capabilitiesSet = new Set(capabilities); - if (capabilitiesSet.has(extHostProtocol.WebviewEditorCapabilities.Editable)) { + const isEditable = capabilitiesSet.has(extHostProtocol.WebviewEditorCapabilities.Editable); + if (isEditable) { model.onUndo(edits => { this._proxy.$undoEdits(resource, viewType, edits.map(x => x.data)); }); @@ -335,10 +338,17 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma e.waitUntil(this._proxy.$onSave(resource.toJSON(), viewType)); }); - model.onWillSaveAs(e => { - e.waitUntil(this._proxy.$onSaveAs(e.resource.toJSON(), viewType, e.targetResource.toJSON())); - }); } + + // Save as should always be implemented even if the model is readonly + model.onWillSaveAs(e => { + if (isEditable) { + e.waitUntil(this._proxy.$onSaveAs(e.resource.toJSON(), viewType, e.targetResource.toJSON())); + } else { + // Since the editor is readonly, just copy the file over + e.waitUntil(this._fileService.copy(e.resource, e.targetResource, false /* overwrite */)); + } + }); return model; } From 22fad2a49ccc70f0ea07c6ef9b23bb25f2e8695d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 10 Jan 2020 18:19:34 -0800 Subject: [PATCH 159/843] Update VS Code build with TS 3.8 beta --- build/package.json | 2 +- build/yarn.lock | 10 +++++----- package.json | 2 +- yarn.lock | 10 +++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build/package.json b/build/package.json index 33c254aafe1..77cfdaf721b 100644 --- a/build/package.json +++ b/build/package.json @@ -43,7 +43,7 @@ "minimist": "^1.2.0", "request": "^2.85.0", "terser": "4.3.8", - "typescript": "^3.8.0-dev.20200108", + "typescript": " 3.8.0-beta", "vsce": "1.48.0", "vscode-telemetry-extractor": "^1.5.4", "xml2js": "^0.4.17" diff --git a/build/yarn.lock b/build/yarn.lock index 38c1cbec7d9..6b7c3ea0068 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2453,16 +2453,16 @@ typed-rest-client@^0.9.0: tunnel "0.0.4" underscore "1.8.3" +"typescript@ 3.8.0-beta": + version "3.8.0-beta" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.0-beta.tgz#acdcaf9f24c7e20b1ff0a6329d1e8d63691e2e13" + integrity sha512-mQEmQUJg0CQBhf/GSVnGscKv/jrKsrLxE01AhdjYmBNoXX2Iah3i38ufxXByXacK6Fc5Nr9oMz7MjpjgddiknA== + typescript@^3.0.1: version "3.5.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== -typescript@^3.8.0-dev.20200108: - version "3.8.0-dev.20200108" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.0-dev.20200108.tgz#ca3a4d950cd19112d80758be779fb07d577e49bc" - integrity sha512-SD3VEYUUrDGc0djorpi0zVdmVwmvuaSHta18WP3sS9X0HC7eA4izdjj07pVUc99IBpBw55ljUATm5vkNdvxX6w== - typical@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" diff --git a/package.json b/package.json index 11d823edf02..922d877c743 100644 --- a/package.json +++ b/package.json @@ -144,7 +144,7 @@ "sinon": "^1.17.2", "source-map": "^0.4.4", "ts-loader": "^4.4.2", - "typescript": "^3.8.0-dev.20200108", + "typescript": "3.8.0-beta", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", diff --git a/yarn.lock b/yarn.lock index 36fee127c84..38a8d55738f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9262,16 +9262,16 @@ typescript-formatter@7.1.0: commandpost "^1.0.0" editorconfig "^0.15.0" +typescript@3.8.0-beta: + version "3.8.0-beta" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.0-beta.tgz#acdcaf9f24c7e20b1ff0a6329d1e8d63691e2e13" + integrity sha512-mQEmQUJg0CQBhf/GSVnGscKv/jrKsrLxE01AhdjYmBNoXX2Iah3i38ufxXByXacK6Fc5Nr9oMz7MjpjgddiknA== + typescript@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= -typescript@^3.8.0-dev.20200108: - version "3.8.0-dev.20200108" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.0-dev.20200108.tgz#ca3a4d950cd19112d80758be779fb07d577e49bc" - integrity sha512-SD3VEYUUrDGc0djorpi0zVdmVwmvuaSHta18WP3sS9X0HC7eA4izdjj07pVUc99IBpBw55ljUATm5vkNdvxX6w== - uc.micro@^1.0.1, uc.micro@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" From ad970cc1b323e9ea8b9d422016a2adb6c890e3f3 Mon Sep 17 00:00:00 2001 From: Fabien Launay Date: Sun, 12 Jan 2020 01:43:47 +0900 Subject: [PATCH 160/843] Fix word repetition in dialogs.ts comment (#88466) --- src/vs/platform/dialogs/common/dialogs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/dialogs/common/dialogs.ts b/src/vs/platform/dialogs/common/dialogs.ts index 68f8ac9c6d4..47ee6ba4c61 100644 --- a/src/vs/platform/dialogs/common/dialogs.ts +++ b/src/vs/platform/dialogs/common/dialogs.ts @@ -230,7 +230,7 @@ export interface IFileDialogService { pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise; /** - * Shows a save file file dialog and save the file at the chosen file URI. + * Shows a save file dialog and save the file at the chosen file URI. */ pickFileToSave(options: ISaveDialogOptions): Promise; From 71df4b20ded9dc724b67817ce941b1550b3f8690 Mon Sep 17 00:00:00 2001 From: Fabien Launay Date: Sun, 12 Jan 2020 01:44:13 +0900 Subject: [PATCH 161/843] Fix word repetition in tokenClassificationExtensionPoint.ts error message (#88468) --- .../services/themes/common/tokenClassificationExtensionPoint.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts b/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts index 77f6a8586c9..d9687a65363 100644 --- a/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts +++ b/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts @@ -228,7 +228,7 @@ export class TokenClassificationExtensionPoints { if (contribution.scopes) { if ((!Array.isArray(contribution.scopes) || contribution.scopes.some(s => typeof s !== 'string'))) { - collector.error(nls.localize('invalid.scopes', "If defined, 'configuration.tokenStyleDefaults.scopes' must must be an array or strings")); + collector.error(nls.localize('invalid.scopes', "If defined, 'configuration.tokenStyleDefaults.scopes' must be an array or strings")); continue; } tokenStyleDefault.scopesToProbe = [contribution.scopes]; From b7c1982aeef05160133d52c9274cb76e2d231b9a Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Sun, 12 Jan 2020 02:39:01 -0500 Subject: [PATCH 162/843] Fixes #88242 --- .../contrib/debug/browser/baseDebugView.ts | 121 +++++++++--------- .../debug/browser/watchExpressionsView.ts | 11 +- 2 files changed, 74 insertions(+), 58 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index c40e105ffef..160c2c8f3cd 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -9,7 +9,7 @@ import { Expression, Variable, ExpressionContainer } from 'vs/workbench/contrib/ import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInputValidationOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -18,6 +18,7 @@ import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabe import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { ReplEvaluationResult } from 'vs/workbench/contrib/debug/common/replModel'; +import { once } from 'vs/base/common/functional'; export const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; export const twistiePixels = 20; @@ -137,8 +138,7 @@ export interface IExpressionTemplateData { name: HTMLSpanElement; value: HTMLSpanElement; inputBoxContainer: HTMLElement; - enableInputBox(options: IInputBoxOptions): void; - toDispose: IDisposable[]; + toDispose: IDisposable; label: HighlightedLabel; } @@ -159,77 +159,84 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer { - name.style.display = 'none'; - value.style.display = 'none'; - inputBoxContainer.style.display = 'initial'; - - const inputBox = new InputBox(inputBoxContainer, this.contextViewService, options); - const styler = attachInputBoxStyler(inputBox, this.themeService); - - inputBox.value = replaceWhitespace(options.initialValue); - inputBox.focus(); - inputBox.select(); - - let disposed = false; - toDispose.push(inputBox); - toDispose.push(styler); - - const wrapUp = (renamed: boolean) => { - if (!disposed) { - disposed = true; - this.debugService.getViewModel().setSelectedExpression(undefined); - options.onFinish(inputBox.value, renamed); - - // need to remove the input box since this template will be reused. - inputBoxContainer.removeChild(inputBox.element); - name.style.display = 'initial'; - value.style.display = 'initial'; - inputBoxContainer.style.display = 'none'; - dispose(toDispose); - } - }; - - toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => { - const isEscape = e.equals(KeyCode.Escape); - const isEnter = e.equals(KeyCode.Enter); - if (isEscape || isEnter) { - e.preventDefault(); - e.stopPropagation(); - wrapUp(isEnter); - } - })); - toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => { - wrapUp(true); - })); - toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'click', e => { - // Do not expand / collapse selected elements - e.preventDefault(); - e.stopPropagation(); - })); - }; - - return { expression, name, value, label, enableInputBox, inputBoxContainer, toDispose }; + return { expression, name, value, label, inputBoxContainer, toDispose: Disposable.None }; } renderElement(node: ITreeNode, index: number, data: IExpressionTemplateData): void { + data.toDispose.dispose(); + data.toDispose = Disposable.None; const { element } = node; if (element === this.debugService.getViewModel().getSelectedExpression() || (element instanceof Variable && element.errorMessage)) { const options = this.getInputBoxOptions(element); if (options) { - data.enableInputBox(options); + data.toDispose = this.renderInputBox(data.name, data.value, data.inputBoxContainer, options); return; } } this.renderExpression(element, data, createMatches(node.filterData)); } + renderInputBox(nameElement: HTMLElement, valueElement: HTMLElement, inputBoxContainer: HTMLElement, options: IInputBoxOptions): IDisposable { + nameElement.style.display = 'none'; + valueElement.style.display = 'none'; + inputBoxContainer.style.display = 'initial'; + + const inputBox = new InputBox(inputBoxContainer, this.contextViewService, options); + const styler = attachInputBoxStyler(inputBox, this.themeService); + + inputBox.value = replaceWhitespace(options.initialValue); + inputBox.focus(); + inputBox.select(); + + const done = once((success: boolean, finishEditing: boolean) => { + nameElement.style.display = 'initial'; + valueElement.style.display = 'initial'; + inputBoxContainer.style.display = 'none'; + const value = inputBox.value; + dispose(toDispose); + + if (finishEditing) { + this.debugService.getViewModel().setSelectedExpression(undefined); + options.onFinish(value, success); + } + }); + + const toDispose = [ + inputBox, + dom.addStandardDisposableListener(inputBox.inputElement, dom.EventType.KEY_DOWN, (e: IKeyboardEvent) => { + const isEscape = e.equals(KeyCode.Escape); + const isEnter = e.equals(KeyCode.Enter); + if (isEscape || isEnter) { + e.preventDefault(); + e.stopPropagation(); + done(isEnter, true); + } + }), + dom.addDisposableListener(inputBox.inputElement, dom.EventType.BLUR, (e) => { + done(true, true); + }), + dom.addDisposableListener(inputBox.inputElement, dom.EventType.CLICK, e => { + // Do not expand / collapse selected elements + e.preventDefault(); + e.stopPropagation(); + }), + styler + ]; + + return toDisposable(() => { + done(false, false); + }); + } + protected abstract renderExpression(expression: IExpression, data: IExpressionTemplateData, highlights: IHighlight[]): void; protected abstract getInputBoxOptions(expression: IExpression): IInputBoxOptions | undefined; + disposeElement(node: ITreeNode, index: number, templateData: IExpressionTemplateData): void { + templateData.toDispose.dispose(); + } + disposeTemplate(templateData: IExpressionTemplateData): void { - dispose(templateData.toDispose); + templateData.toDispose.dispose(); } } diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index 0e2f8fd6df4..7ee2d0fcaca 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -69,7 +69,16 @@ export class WatchExpressionsView extends ViewPane { ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"), accessibilityProvider: new WatchExpressionsAccessibilityProvider(), identityProvider: { getId: (element: IExpression) => element.getId() }, - keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IExpression) => e }, + keyboardNavigationLabelProvider: { + getKeyboardNavigationLabel: (e: IExpression) => { + if (e === this.debugService.getViewModel().getSelectedExpression()) { + // Don't filter input box + return undefined; + } + + return e; + } + }, dnd: new WatchExpressionsDragAndDrop(this.debugService), overrideStyles: { listBackground: SIDE_BAR_BACKGROUND From df4a71a8708089380e25ba9f51bf2aa15226c3a8 Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Sun, 12 Jan 2020 02:50:09 -0500 Subject: [PATCH 163/843] :lipstick: --- src/vs/workbench/contrib/debug/browser/baseDebugView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index 160c2c8f3cd..3d6eafae60a 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -213,7 +213,7 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer { + dom.addDisposableListener(inputBox.inputElement, dom.EventType.BLUR, () => { done(true, true); }), dom.addDisposableListener(inputBox.inputElement, dom.EventType.CLICK, e => { From e6651b90f937e9874aeba717cb3724190e01a34f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 12 Jan 2020 11:11:41 +0100 Subject: [PATCH 164/843] custom editors - implement auto save and backup creation over working copy events (#84672) --- src/vs/platform/files/common/files.ts | 2 - .../browser/parts/editor/editorAutoSave.ts | 75 ++++++-- src/vs/workbench/common/editor.ts | 6 +- .../common/editor/untitledTextEditorInput.ts | 14 +- .../common/editor/untitledTextEditorModel.ts | 30 +-- .../backup/common/backup.contribution.ts | 8 +- .../backup/common/backupModelTracker.ts | 85 --------- .../contrib/backup/common/backupTracker.ts | 124 +++++++++++++ .../electron-browser/backupRestorer.test.ts | 8 +- .../electron-browser/backupTracker.test.ts | 174 ++++++++++++++++++ .../customEditor/common/customEditorModel.ts | 19 ++ .../common/customEditorModelManager.ts | 2 +- .../test/browser/fileEditorTracker.test.ts | 37 ++-- .../backupFileService.test.ts | 31 +++- .../editor/test/browser/editorService.test.ts | 114 +++--------- .../test/browser/editorsObserver.test.ts | 84 +++------ .../common/filesConfigurationService.ts | 4 +- .../textfile/browser/textFileService.ts | 59 +++--- .../textfile/common/textFileEditorModel.ts | 105 +++++------ .../common/textFileEditorModelManager.ts | 27 +-- .../services/textfile/common/textfiles.ts | 10 +- .../textfile/test/textFileEditorModel.test.ts | 42 ++++- .../test/textFileEditorModelManager.test.ts | 27 --- .../common/untitledTextEditorService.ts | 21 --- .../workingCopy/common/workingCopyService.ts | 51 ++++- .../test/common/workingCopyService.test.ts | 38 ++++ .../common/editor/untitledTextEditor.test.ts | 64 ++++--- 27 files changed, 722 insertions(+), 539 deletions(-) delete mode 100644 src/vs/workbench/contrib/backup/common/backupModelTracker.ts create mode 100644 src/vs/workbench/contrib/backup/common/backupTracker.ts create mode 100644 src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index a45eb2016cf..86252c89389 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -787,8 +787,6 @@ export const HotExitConfiguration = { ON_EXIT_AND_WINDOW_CLOSE: 'onExitAndWindowClose' }; -export const CONTENT_CHANGE_EVENT_BUFFER_DELAY = 1000; - export const FILES_ASSOCIATIONS_CONFIG = 'files.associations'; export const FILES_EXCLUDE_CONFIG = 'files.exclude'; diff --git a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts index fd7bbf92a66..6be968e338f 100644 --- a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts +++ b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts @@ -45,7 +45,12 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution this._register(this.hostService.onDidChangeFocus(focused => this.onWindowFocusChange(focused))); this._register(this.editorService.onDidActiveEditorChange(() => this.onDidActiveEditorChange())); this._register(this.filesConfigurationService.onAutoSaveConfigurationChange(config => this.onAutoSaveConfigurationChange(config, true))); - this._register(this.workingCopyService.onDidChangeDirty(workingCopy => this.onDidWorkingCopyChangeDirty(workingCopy))); + + // Working Copy events + this._register(this.workingCopyService.onDidRegister(c => this.onDidRegister(c))); + this._register(this.workingCopyService.onDidUnregister(c => this.onDidUnregister(c))); + this._register(this.workingCopyService.onDidChangeDirty(c => this.onDidChangeDirty(c))); + this._register(this.workingCopyService.onDidChangeContent(c => this.onDidChangeContent(c))); } private onWindowFocusChange(focused: boolean): void { @@ -128,14 +133,34 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution } private saveAllDirty(options?: ISaveOptions): void { - Promise.all(this.workingCopyService.workingCopies.map(workingCopy => { + for (const workingCopy of this.workingCopyService.workingCopies) { if (workingCopy.isDirty() && !(workingCopy.capabilities & WorkingCopyCapabilities.Untitled)) { workingCopy.save(options); } - })); + } } - private onDidWorkingCopyChangeDirty(workingCopy: IWorkingCopy): void { + private onDidRegister(workingCopy: IWorkingCopy): void { + this.scheduleAutoSave(workingCopy); + } + + private onDidUnregister(workingCopy: IWorkingCopy): void { + this.discardAutoSave(workingCopy); + } + + private onDidChangeDirty(workingCopy: IWorkingCopy): void { + if (!workingCopy.isDirty()) { + this.discardAutoSave(workingCopy); + } + } + + private onDidChangeContent(workingCopy: IWorkingCopy): void { + if (workingCopy.isDirty()) { + this.scheduleAutoSave(workingCopy); + } + } + + private scheduleAutoSave(workingCopy: IWorkingCopy): void { if (typeof this.autoSaveAfterDelay !== 'number') { return; // auto save after delay must be enabled } @@ -145,22 +170,34 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution } // Clear any running auto save operation + this.discardAutoSave(workingCopy); + + this.logService.trace(`[editor auto save] scheduling auto save after ${this.autoSaveAfterDelay}ms`, workingCopy.resource.toString()); + + // Schedule new auto save + const handle = setTimeout(() => { + + // Clear disposable + this.pendingAutoSavesAfterDelay.delete(workingCopy); + + // Save if dirty + if (workingCopy.isDirty()) { + this.logService.trace(`[editor auto save] running auto save`, workingCopy.resource.toString()); + + workingCopy.save({ reason: SaveReason.AUTO }); + } + }, this.autoSaveAfterDelay); + + // Keep in map for disposal as needed + this.pendingAutoSavesAfterDelay.set(workingCopy, toDisposable(() => { + this.logService.trace(`[editor auto save] clearing pending auto save`, workingCopy.resource.toString()); + + clearTimeout(handle); + })); + } + + private discardAutoSave(workingCopy: IWorkingCopy): void { dispose(this.pendingAutoSavesAfterDelay.get(workingCopy)); this.pendingAutoSavesAfterDelay.delete(workingCopy); - - // Working copy got dirty - start auto save - if (workingCopy.isDirty()) { - this.logService.trace(`[editor auto save] starting auto save after ${this.autoSaveAfterDelay}ms`, workingCopy.resource.toString()); - - const handle = setTimeout(() => { - if (workingCopy.isDirty()) { - workingCopy.save({ reason: SaveReason.AUTO }); - } - }, this.autoSaveAfterDelay); - - this.pendingAutoSavesAfterDelay.set(workingCopy, toDisposable(() => clearTimeout(handle))); - } else { - this.logService.trace(`[editor auto save] clearing auto save`, workingCopy.resource.toString()); - } } } diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 92b1874ffcf..6a55d4bd697 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -317,7 +317,7 @@ export interface ISaveOptions { context?: SaveContext; /** - * Forces to load the contents of the working copy + * Forces to save the contents of the working copy * again even if the working copy is not dirty. */ force?: boolean; @@ -571,10 +571,6 @@ export abstract class TextEditorInput extends EditorInput { } async save(groupId: GroupIdentifier, options?: ITextFileSaveOptions): Promise { - if (this.isReadonly()) { - return false; // return early if editor is readonly - } - return this.textFileService.save(this.resource, options); } diff --git a/src/vs/workbench/common/editor/untitledTextEditorInput.ts b/src/vs/workbench/common/editor/untitledTextEditorInput.ts index cd3091c5081..2366bcf133b 100644 --- a/src/vs/workbench/common/editor/untitledTextEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledTextEditorInput.ts @@ -28,9 +28,6 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin private static readonly MEMOIZER = createMemoizer(); - private readonly _onDidModelChangeContent = this._register(new Emitter()); - readonly onDidModelChangeContent = this._onDidModelChangeContent.event; - private readonly _onDidModelChangeEncoding = this._register(new Emitter()); readonly onDidModelChangeEncoding = this._onDidModelChangeEncoding.event; @@ -147,6 +144,8 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin } isDirty(): boolean { + + // Always trust the model first if existing if (this.cachedModel) { return this.cachedModel.isDirty(); } @@ -156,7 +155,13 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin return false; } - // untitled files with an associated path or associated resource + // A input with initial value is always dirty + if (this.initialValue && this.initialValue.length > 0) { + return true; + } + + // A input with associated path is always dirty because it is the intent + // of the user to create a new file at that location through saving return this.hasAssociatedFilePath; } @@ -274,7 +279,6 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin const model = this._register(this.instantiationService.createInstance(UntitledTextEditorModel, this.preferredMode, this.resource, this.hasAssociatedFilePath, this.initialValue, this.preferredEncoding)); // re-emit some events from the model - this._register(model.onDidChangeContent(() => this._onDidModelChangeContent.fire())); this._register(model.onDidChangeDirty(() => this._onDidChangeDirty.fire())); this._register(model.onDidChangeEncoding(() => this._onDidModelChangeEncoding.fire())); diff --git a/src/vs/workbench/common/editor/untitledTextEditorModel.ts b/src/vs/workbench/common/editor/untitledTextEditorModel.ts index a34992acbc9..a110d1f782f 100644 --- a/src/vs/workbench/common/editor/untitledTextEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledTextEditorModel.ts @@ -6,11 +6,9 @@ import { IEncodingSupport, ISaveOptions } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { URI } from 'vs/base/common/uri'; -import { CONTENT_CHANGE_EVENT_BUFFER_DELAY } from 'vs/platform/files/common/files'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { Event, Emitter } from 'vs/base/common/event'; -import { RunOnceScheduler } from 'vs/base/common/async'; +import { Emitter } from 'vs/base/common/event'; import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { ITextBufferFactory } from 'vs/editor/common/model'; @@ -21,22 +19,19 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile export class UntitledTextEditorModel extends BaseTextEditorModel implements IEncodingSupport, IWorkingCopy { - static DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = CONTENT_CHANGE_EVENT_BUFFER_DELAY; + private readonly _onDidChangeContent = this._register(new Emitter()); + readonly onDidChangeContent = this._onDidChangeContent.event; - private readonly _onDidChangeContent: Emitter = this._register(new Emitter()); - readonly onDidChangeContent: Event = this._onDidChangeContent.event; + private readonly _onDidChangeDirty = this._register(new Emitter()); + readonly onDidChangeDirty = this._onDidChangeDirty.event; - private readonly _onDidChangeDirty: Emitter = this._register(new Emitter()); - readonly onDidChangeDirty: Event = this._onDidChangeDirty.event; - - private readonly _onDidChangeEncoding: Emitter = this._register(new Emitter()); - readonly onDidChangeEncoding: Event = this._onDidChangeEncoding.event; + private readonly _onDidChangeEncoding = this._register(new Emitter()); + readonly onDidChangeEncoding = this._onDidChangeEncoding.event; readonly capabilities = WorkingCopyCapabilities.Untitled; private dirty = false; private versionId = 0; - private readonly contentChangeEventScheduler = this._register(new RunOnceScheduler(() => this._onDidChangeContent.fire(), UntitledTextEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY)); private configuredEncoding: string | undefined; constructor( @@ -124,18 +119,13 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IEnc async revert(): Promise { this.setDirty(false); - // Handle content change event buffered - this.contentChangeEventScheduler.schedule(); - return true; } - backup(): Promise { + async backup(): Promise { if (this.isResolved()) { return this.backupFileService.backupResource(this.resource, this.createSnapshot(), this.versionId); } - - return Promise.resolve(); } hasBackup(): boolean { @@ -204,8 +194,8 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IEnc this.setDirty(true); } - // Handle content change event buffered - this.contentChangeEventScheduler.schedule(); + // Emit as event + this._onDidChangeContent.fire(); } isReadonly(): boolean { diff --git a/src/vs/workbench/contrib/backup/common/backup.contribution.ts b/src/vs/workbench/contrib/backup/common/backup.contribution.ts index f3ac6e3a0bd..9800a1c9472 100644 --- a/src/vs/workbench/contrib/backup/common/backup.contribution.ts +++ b/src/vs/workbench/contrib/backup/common/backup.contribution.ts @@ -5,12 +5,12 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { BackupModelTracker } from 'vs/workbench/contrib/backup/common/backupModelTracker'; +import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker'; import { BackupRestorer } from 'vs/workbench/contrib/backup/common/backupRestorer'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -// Register Backup Model Tracker -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BackupModelTracker, LifecyclePhase.Starting); +// Register Backup Tracker +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BackupTracker, LifecyclePhase.Starting); // Register Backup Restorer -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BackupRestorer, LifecyclePhase.Starting); \ No newline at end of file +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BackupRestorer, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/backup/common/backupModelTracker.ts b/src/vs/workbench/contrib/backup/common/backupModelTracker.ts deleted file mode 100644 index 2f44e59ad78..00000000000 --- a/src/vs/workbench/contrib/backup/common/backupModelTracker.ts +++ /dev/null @@ -1,85 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { URI as Uri } from 'vs/base/common/uri'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { ITextFileService, TextFileModelChangeEvent, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; -import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { CONTENT_CHANGE_EVENT_BUFFER_DELAY } from 'vs/platform/files/common/files'; -import { IFilesConfigurationService, IAutoSaveConfiguration } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; - -const AUTO_SAVE_AFTER_DELAY_DISABLED_TIME = CONTENT_CHANGE_EVENT_BUFFER_DELAY + 500; - -export class BackupModelTracker extends Disposable implements IWorkbenchContribution { - - private configuredAutoSaveAfterDelay = false; - - constructor( - @IBackupFileService private readonly backupFileService: IBackupFileService, - @ITextFileService private readonly textFileService: ITextFileService, - @IUntitledTextEditorService private readonly untitledTextEditorService: IUntitledTextEditorService, - @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService - ) { - super(); - - this.registerListeners(); - } - - private registerListeners() { - - // Listen for text file model changes - this._register(this.textFileService.models.onModelContentChanged(e => this.onTextFileModelChanged(e))); - this._register(this.textFileService.models.onModelSaved(e => this.discardBackup(e.resource))); - this._register(this.textFileService.models.onModelDisposed(e => this.discardBackup(e))); - - // Listen for untitled model changes - this._register(this.untitledTextEditorService.onDidCreate(e => this.onUntitledModelCreated(e))); - this._register(this.untitledTextEditorService.onDidChangeContent(e => this.onUntitledModelChanged(e))); - this._register(this.untitledTextEditorService.onDidDisposeModel(e => this.discardBackup(e))); - - // Listen to auto save config changes - this._register(this.filesConfigurationService.onAutoSaveConfigurationChange(c => this.onAutoSaveConfigurationChange(c))); - } - - private onAutoSaveConfigurationChange(configuration: IAutoSaveConfiguration): void { - this.configuredAutoSaveAfterDelay = typeof configuration.autoSaveDelay === 'number' && configuration.autoSaveDelay < AUTO_SAVE_AFTER_DELAY_DISABLED_TIME; - } - - private onTextFileModelChanged(event: TextFileModelChangeEvent): void { - if (event.kind === StateChange.REVERTED) { - // This must proceed even if auto save after delay is configured in order to clean up - // any backups made before the config change - this.discardBackup(event.resource); - } else if (event.kind === StateChange.CONTENT_CHANGE) { - // Do not backup when auto save after delay is configured - if (!this.configuredAutoSaveAfterDelay) { - const model = this.textFileService.models.get(event.resource); - if (model) { - model.backup(); - } - } - } - } - - private onUntitledModelCreated(resource: Uri): void { - if (this.untitledTextEditorService.isDirty(resource)) { - this.untitledTextEditorService.createOrGet({ resource }).resolve().then(model => model.backup()); - } - } - - private onUntitledModelChanged(resource: Uri): void { - if (this.untitledTextEditorService.isDirty(resource)) { - this.untitledTextEditorService.createOrGet({ resource }).resolve().then(model => model.backup()); - } else { - this.discardBackup(resource); - } - } - - private discardBackup(resource: Uri): void { - this.backupFileService.discardResourceBackup(resource); - } -} diff --git a/src/vs/workbench/contrib/backup/common/backupTracker.ts b/src/vs/workbench/contrib/backup/common/backupTracker.ts new file mode 100644 index 00000000000..8496b31a7cb --- /dev/null +++ b/src/vs/workbench/contrib/backup/common/backupTracker.ts @@ -0,0 +1,124 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IFilesConfigurationService, IAutoSaveConfiguration } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { ILogService } from 'vs/platform/log/common/log'; + +export class BackupTracker extends Disposable implements IWorkbenchContribution { + + // Disable backup for when a short auto-save delay is configured with + // the rationale that the auto save will trigger a save periodically + // anway and thus creating frequent backups is not useful + // + // This will only apply to working copies that are not untitled where + // auto save is actually saving. + private static DISABLE_BACKUP_AUTO_SAVE_THRESHOLD = 1500; + + // Delay creation of backups when content changes to avoid too much + // load on the backup service when the user is typing into the editor + protected static BACKUP_FROM_CONTENT_CHANGE_DELAY = 1000; + + private backupsDisabledForAutoSaveables = false; + + private readonly pendingBackups = new Map(); + + constructor( + @IBackupFileService private readonly backupFileService: IBackupFileService, + @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, + @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, + @ILogService private readonly logService: ILogService + ) { + super(); + + // Figure out initial auto save config + this.onAutoSaveConfigurationChange(filesConfigurationService.getAutoSaveConfiguration()); + + this.registerListeners(); + } + + private registerListeners() { + + // Working Copy events + this._register(this.workingCopyService.onDidRegister(c => this.onDidRegister(c))); + this._register(this.workingCopyService.onDidUnregister(c => this.onDidUnregister(c))); + this._register(this.workingCopyService.onDidChangeDirty(c => this.onDidChangeDirty(c))); + this._register(this.workingCopyService.onDidChangeContent(c => this.onDidChangeContent(c))); + + // Listen to auto save config changes + this._register(this.filesConfigurationService.onAutoSaveConfigurationChange(c => this.onAutoSaveConfigurationChange(c))); + } + + private onDidRegister(workingCopy: IWorkingCopy): void { + this.scheduleBackup(workingCopy); + } + + private onDidUnregister(workingCopy: IWorkingCopy): void { + this.discardBackup(workingCopy); + } + + private onDidChangeDirty(workingCopy: IWorkingCopy): void { + if (!workingCopy.isDirty()) { + this.discardBackup(workingCopy); + } + } + + private onDidChangeContent(workingCopy: IWorkingCopy): void { + if (workingCopy.isDirty()) { + this.scheduleBackup(workingCopy); + } + } + + private onAutoSaveConfigurationChange(configuration: IAutoSaveConfiguration): void { + this.backupsDisabledForAutoSaveables = typeof configuration.autoSaveDelay === 'number' && configuration.autoSaveDelay < BackupTracker.DISABLE_BACKUP_AUTO_SAVE_THRESHOLD; + } + + private scheduleBackup(workingCopy: IWorkingCopy): void { + if (this.backupsDisabledForAutoSaveables && !(workingCopy.capabilities & WorkingCopyCapabilities.Untitled)) { + return; // skip if auto save is enabled with a short delay + } + + // Clear any running backup operation + dispose(this.pendingBackups.get(workingCopy)); + this.pendingBackups.delete(workingCopy); + + this.logService.trace(`[backup tracker] scheduling backup`, workingCopy.resource.toString()); + + // Schedule new backup + const handle = setTimeout(() => { + + // Clear disposable + this.pendingBackups.delete(workingCopy); + + // Backup if dirty + if (workingCopy.isDirty()) { + this.logService.trace(`[backup tracker] running backup`, workingCopy.resource.toString()); + + workingCopy.backup(); + } + }, BackupTracker.BACKUP_FROM_CONTENT_CHANGE_DELAY); + + // Keep in map for disposal as needed + this.pendingBackups.set(workingCopy, toDisposable(() => { + this.logService.trace(`[backup tracker] clearing pending backup`, workingCopy.resource.toString()); + + clearTimeout(handle); + })); + } + + private discardBackup(workingCopy: IWorkingCopy): void { + this.logService.trace(`[backup tracker] discarding backup`, workingCopy.resource.toString()); + + // Clear any running backup operation + dispose(this.pendingBackups.get(workingCopy)); + this.pendingBackups.delete(workingCopy); + + // Forward to backup file service + this.backupFileService.discardResourceBackup(workingCopy.resource); + } +} diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts index cc5d10a70a4..e3fa6b8c44e 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts @@ -14,7 +14,7 @@ import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { DefaultEndOfLine } from 'vs/editor/common/model'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { hashPath } from 'vs/workbench/services/backup/node/backupFileService'; -import { BackupModelTracker } from 'vs/workbench/contrib/backup/common/backupModelTracker'; +import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker'; import { TestTextFileService, workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; @@ -60,7 +60,7 @@ class ServiceAccessor { } } -suite('BackupModelRestorer', () => { +suite('BackupRestorer', () => { let accessor: ServiceAccessor; let disposables: IDisposable[] = []; @@ -111,7 +111,9 @@ suite('BackupModelRestorer', () => { accessor = instantiationService.createInstance(ServiceAccessor); - const tracker = instantiationService.createInstance(BackupModelTracker); + await part.whenRestored; + + const tracker = instantiationService.createInstance(BackupTracker); const restorer = instantiationService.createInstance(TestBackupRestorer); // Backup 2 normal files and 2 untitled file diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts new file mode 100644 index 00000000000..945cb8352de --- /dev/null +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts @@ -0,0 +1,174 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as platform from 'vs/base/common/platform'; +import * as os from 'os'; +import * as path from 'vs/base/common/path'; +import * as pfs from 'vs/base/node/pfs'; +import { URI } from 'vs/base/common/uri'; +import { getRandomTestPath } from 'vs/base/test/node/testUtils'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { hashPath } from 'vs/workbench/services/backup/node/backupFileService'; +import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker'; +import { TestTextFileService, workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; +import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; +import { TextFileEditor } from 'vs/workbench/contrib/files/browser/editors/textFileEditor'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { NodeTestBackupFileService } from 'vs/workbench/services/backup/test/electron-browser/backupFileService.test'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { toResource } from 'vs/base/test/common/utils'; +import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { ILogService } from 'vs/platform/log/common/log'; + +const userdataDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backuprestorer'); +const backupHome = path.join(userdataDir, 'Backups'); +const workspacesJsonPath = path.join(backupHome, 'workspaces.json'); + +const workspaceResource = URI.file(platform.isWindows ? 'c:\\workspace' : '/workspace'); +const workspaceBackupPath = path.join(backupHome, hashPath(workspaceResource)); + +class ServiceAccessor { + constructor( + @ITextFileService public textFileService: TestTextFileService, + @IUntitledTextEditorService public untitledTextEditorService: IUntitledTextEditorService, + @IEditorService public editorService: IEditorService, + @IBackupFileService public backupFileService: NodeTestBackupFileService + ) { + } +} + +class TestBackupTracker extends BackupTracker { + + constructor( + @IBackupFileService backupFileService: IBackupFileService, + @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, + @IWorkingCopyService workingCopyService: IWorkingCopyService, + @ILogService logService: ILogService + ) { + super(backupFileService, filesConfigurationService, workingCopyService, logService); + + // Reduce timeout for tests + BackupTracker.BACKUP_FROM_CONTENT_CHANGE_DELAY = 10; + } +} + +suite('BackupTracker', () => { + let accessor: ServiceAccessor; + + let disposables: IDisposable[] = []; + + setup(async () => { + disposables.push(Registry.as(EditorExtensions.Editors).registerEditor( + EditorDescriptor.create( + TextFileEditor, + TextFileEditor.ID, + 'Text File Editor' + ), + [new SyncDescriptor(FileEditorInput)] + )); + + // Delete any existing backups completely and then re-create it. + await pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); + await pfs.mkdirp(backupHome); + + return pfs.writeFile(workspacesJsonPath, ''); + }); + + teardown(async () => { + dispose(disposables); + disposables = []; + + (accessor.textFileService.models).clear(); + (accessor.textFileService.models).dispose(); + accessor.untitledTextEditorService.revertAll(); + + return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); + }); + + async function createTracker(): Promise<[ServiceAccessor, EditorPart, BackupTracker]> { + const backupFileService = new NodeTestBackupFileService(workspaceBackupPath); + const instantiationService = workbenchInstantiationService(); + instantiationService.stub(IBackupFileService, backupFileService); + + const part = instantiationService.createInstance(EditorPart); + part.create(document.createElement('div')); + part.layout(400, 300); + + instantiationService.stub(IEditorGroupsService, part); + + const editorService: EditorService = instantiationService.createInstance(EditorService); + instantiationService.stub(IEditorService, editorService); + + accessor = instantiationService.createInstance(ServiceAccessor); + + await part.whenRestored; + + const tracker = instantiationService.createInstance(TestBackupTracker); + + return [accessor, part, tracker]; + } + + test('Track backups (untitled)', async function () { + this.timeout(20000); + + const [accessor, part, tracker] = await createTracker(); + + const untitledEditor = accessor.untitledTextEditorService.createOrGet(); + await accessor.editorService.openEditor(untitledEditor, { pinned: true }); + + const untitledModel = await untitledEditor.resolve(); + untitledModel.textEditorModel.setValue('Super Good'); + + await accessor.backupFileService.joinBackupResource(); + + assert.equal(accessor.backupFileService.hasBackupSync(untitledEditor.getResource()), true); + + untitledModel.dispose(); + + await accessor.backupFileService.joinDiscardBackup(); + + assert.equal(accessor.backupFileService.hasBackupSync(untitledEditor.getResource()), false); + + part.dispose(); + tracker.dispose(); + }); + + test('Track backups (file)', async function () { + this.timeout(20000); + + const [accessor, part, tracker] = await createTracker(); + + const resource = toResource.call(this, '/path/index.txt'); + await accessor.editorService.openEditor({ resource, options: { pinned: true } }); + + const fileModel = accessor.textFileService.models.get(resource); + fileModel?.textEditorModel?.setValue('Super Good'); + + await accessor.backupFileService.joinBackupResource(); + + assert.equal(accessor.backupFileService.hasBackupSync(resource), true); + + fileModel?.dispose(); + + await accessor.backupFileService.joinDiscardBackup(); + + assert.equal(accessor.backupFileService.hasBackupSync(resource), false); + + part.dispose(); + tracker.dispose(); + }); +}); diff --git a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts index e4913f6962c..6f7de36e6b3 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts @@ -39,6 +39,9 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel protected readonly _onDidChangeDirty: Emitter = this._register(new Emitter()); readonly onDidChangeDirty: Event = this._onDidChangeDirty.event; + protected readonly _onDidChangeContent: Emitter = this._register(new Emitter()); + readonly onDidChangeContent: Event = this._onDidChangeContent.event; + //#endregion protected readonly _onUndo = this._register(new Emitter()); @@ -62,12 +65,21 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel this._currentEditIndex = this._edits.length - 1; this.updateDirty(); this._onApplyEdit.fire([edit]); + this.updateContentChanged(); } private updateDirty() { + // TODO@matt this should to be more fine grained and avoid + // emitting events if there was no change actually this._onDidChangeDirty.fire(); } + private updateContentChanged() { + // TODO@matt revisit that this method is being called correctly + // on each case of content change within the custom editor + this._onDidChangeContent.fire(); + } + public async save(_options?: ISaveOptions): Promise { const untils: Promise[] = []; const handler: CustomEditorSaveEvent = { @@ -125,6 +137,7 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel this._currentEditIndex = this._savePoint; this._edits.splice(this._currentEditIndex + 1, this._edits.length - this._currentEditIndex); this.updateDirty(); + this.updateContentChanged(); return true; } @@ -139,6 +152,7 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel this._onUndo.fire([{ data: undoneEdit }]); this.updateDirty(); + this.updateContentChanged(); } public redo() { @@ -153,5 +167,10 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel this._onApplyEdit.fire([{ data: redoneEdit }]); this.updateDirty(); + this.updateContentChanged(); + } + + public async backup(): Promise { + //TODO@matt forward to extension } } diff --git a/src/vs/workbench/contrib/customEditor/common/customEditorModelManager.ts b/src/vs/workbench/contrib/customEditor/common/customEditorModelManager.ts index b97897df8c4..62271532b12 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditorModelManager.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditorModelManager.ts @@ -29,7 +29,7 @@ export class CustomEditorModelManager implements ICustomEditorModelManager { const model = new CustomEditorModel(resource); const disposables = new DisposableStore(); - this._workingCopyService.registerWorkingCopy(model); + disposables.add(this._workingCopyService.registerWorkingCopy(model)); this._models.set(this.key(resource, viewType), { model, disposables }); return model; } diff --git a/src/vs/workbench/contrib/files/test/browser/fileEditorTracker.test.ts b/src/vs/workbench/contrib/files/test/browser/fileEditorTracker.test.ts index 8c06d19a919..f0362d48c0f 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileEditorTracker.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileEditorTracker.test.ts @@ -83,7 +83,7 @@ suite('Files - FileEditorTracker', () => { (accessor.textFileService.models).dispose(); }); - test('dirty text file model opens as editor', async function () { + async function createTracker(): Promise<[EditorPart, ServiceAccessor, FileEditorTracker]> { const instantiationService = workbenchInstantiationService(); const part = instantiationService.createInstance(EditorPart); @@ -97,18 +97,26 @@ suite('Files - FileEditorTracker', () => { const accessor = instantiationService.createInstance(ServiceAccessor); + await part.whenRestored; + const tracker = instantiationService.createInstance(FileEditorTracker); + return [part, accessor, tracker]; + } + + test('dirty text file model opens as editor', async function () { + const [part, accessor, tracker] = await createTracker(); + const resource = toResource.call(this, '/path/index.txt'); - assert.ok(!editorService.isOpen({ resource })); + assert.ok(!accessor.editorService.isOpen({ resource })); const model = await accessor.textFileService.models.loadOrCreate(resource) as IResolvedTextFileEditorModel; model.textEditorModel.setValue('Super Good'); - await awaitEditorOpening(editorService); - assert.ok(editorService.isOpen({ resource })); + await awaitEditorOpening(accessor.editorService); + assert.ok(accessor.editorService.isOpen({ resource })); part.dispose(); tracker.dispose(); @@ -117,30 +125,17 @@ suite('Files - FileEditorTracker', () => { }); test('dirty untitled text file model opens as editor', async function () { - const instantiationService = workbenchInstantiationService(); - - const part = instantiationService.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - instantiationService.stub(IEditorGroupsService, part); - - const editorService: EditorService = instantiationService.createInstance(EditorService); - instantiationService.stub(IEditorService, editorService); - - const accessor = instantiationService.createInstance(ServiceAccessor); - - const tracker = instantiationService.createInstance(FileEditorTracker); + const [part, accessor, tracker] = await createTracker(); const untitledEditor = accessor.untitledTextEditorService.createOrGet(); const model = await untitledEditor.resolve(); - assert.ok(!editorService.isOpen(untitledEditor)); + assert.ok(!accessor.editorService.isOpen(untitledEditor)); model.textEditorModel.setValue('Super Good'); - await awaitEditorOpening(editorService); - assert.ok(editorService.isOpen(untitledEditor)); + await awaitEditorOpening(accessor.editorService); + assert.ok(accessor.editorService.isOpen(untitledEditor)); part.dispose(); tracker.dispose(); diff --git a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts index f49293fd2ab..bd0644729e8 100644 --- a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts @@ -14,7 +14,7 @@ import { URI } from 'vs/base/common/uri'; import { BackupFilesModel } from 'vs/workbench/services/backup/common/backupFileService'; import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; -import { DefaultEndOfLine } from 'vs/editor/common/model'; +import { DefaultEndOfLine, ITextSnapshot } from 'vs/editor/common/model'; import { Schemas } from 'vs/base/common/network'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { FileService } from 'vs/platform/files/common/fileService'; @@ -57,6 +57,9 @@ export class NodeTestBackupFileService extends BackupFileService { readonly fileService: IFileService; + private backupResourceJoiners: Function[]; + private discardBackupJoiners: Function[]; + constructor(workspaceBackupPath: string) { const environmentService = new TestBackupEnvironmentService(workspaceBackupPath); const fileService = new FileService(new NullLogService()); @@ -67,11 +70,37 @@ export class NodeTestBackupFileService extends BackupFileService { super(environmentService, fileService); this.fileService = fileService; + this.backupResourceJoiners = []; + this.discardBackupJoiners = []; } toBackupResource(resource: URI): URI { return super.toBackupResource(resource); } + + joinBackupResource(): Promise { + return new Promise(resolve => this.backupResourceJoiners.push(resolve)); + } + + async backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: any): Promise { + await super.backupResource(resource, content, versionId, meta); + + while (this.backupResourceJoiners.length) { + this.backupResourceJoiners.pop()!(); + } + } + + joinDiscardBackup(): Promise { + return new Promise(resolve => this.discardBackupJoiners.push(resolve)); + } + + async discardResourceBackup(resource: URI): Promise { + await super.discardResourceBackup(resource); + + while (this.discardBackupJoiners.length) { + this.discardBackupJoiners.pop()!(); + } + } } suite('BackupFileService', () => { diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index fe74fba0aea..67ded3c54dc 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -14,14 +14,12 @@ import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService import { EditorService, DelegatingEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IEditorGroup, IEditorGroupsService, GroupDirection, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IEditorRegistry, EditorDescriptor, Extensions } from 'vs/workbench/browser/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Registry } from 'vs/platform/registry/common/platform'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput'; -import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; import { timeout } from 'vs/base/common/async'; import { toResource } from 'vs/base/test/common/utils'; import { IFileService } from 'vs/platform/files/common/files'; @@ -32,6 +30,7 @@ import { NullFileSystemProvider } from 'vs/platform/files/test/common/nullFileSy import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; const TEST_EDITOR_ID = 'MyTestEditorForEditorService'; const TEST_EDITOR_INPUT_ID = 'testEditorInputForEditorService'; @@ -120,16 +119,23 @@ suite('EditorService', () => { disposables = []; }); - test('basics', async () => { - const partInstantiator = workbenchInstantiationService(); + function createEditorService(): [EditorPart, EditorService, IInstantiationService] { + const instantiationService = workbenchInstantiationService(); - const part = partInstantiator.createInstance(EditorPart); + const part = instantiationService.createInstance(EditorPart); part.create(document.createElement('div')); part.layout(400, 300); - const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); + instantiationService.stub(IEditorGroupsService, part); - const service: EditorServiceImpl = testInstantiationService.createInstance(EditorService); + const editorService = instantiationService.createInstance(EditorService); + instantiationService.stub(IEditorService, editorService); + + return [part, editorService, instantiationService]; + } + + test('basics', async () => { + const [part, service, testInstantiationService] = createEditorService(); let input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-basics')); let otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-basics')); @@ -212,15 +218,7 @@ suite('EditorService', () => { }); test('openEditors() / replaceEditors()', async () => { - const partInstantiator = workbenchInstantiationService(); - - const part = partInstantiator.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); - - const service: IEditorService = testInstantiationService.createInstance(EditorService); + const [part, service, testInstantiationService] = createEditorService(); const input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-openEditors')); const otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-openEditors')); @@ -242,7 +240,7 @@ suite('EditorService', () => { test('caching', function () { const instantiationService = workbenchInstantiationService(); - const service: EditorService = instantiationService.createInstance(EditorService); + const service = instantiationService.createInstance(EditorService); // Cached Input (Files) const fileResource1 = toResource.call(this, '/foo/bar/cache1.js'); @@ -291,7 +289,7 @@ suite('EditorService', () => { test('createInput', async function () { const instantiationService = workbenchInstantiationService(); - const service: EditorService = instantiationService.createInstance(EditorService); + const service = instantiationService.createInstance(EditorService); const mode = 'create-input-test'; ModesRegistry.registerLanguage({ @@ -395,15 +393,7 @@ suite('EditorService', () => { }); test('close editor does not dispose when editor opened in other group', async () => { - const partInstantiator = workbenchInstantiationService(); - - const part = partInstantiator.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); - - const service: IEditorService = testInstantiationService.createInstance(EditorService); + const [part, service, testInstantiationService] = createEditorService(); const input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-close1')); @@ -432,15 +422,7 @@ suite('EditorService', () => { }); test('open to the side', async () => { - const partInstantiator = workbenchInstantiationService(); - - const part = partInstantiator.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); - - const service: IEditorService = testInstantiationService.createInstance(EditorService); + const [part, service, testInstantiationService] = createEditorService(); const input1 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource1-openside')); const input2 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-openside')); @@ -466,15 +448,7 @@ suite('EditorService', () => { }); test('editor group activation', async () => { - const partInstantiator = workbenchInstantiationService(); - - const part = partInstantiator.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); - - const service: IEditorService = testInstantiationService.createInstance(EditorService); + const [part, service, testInstantiationService] = createEditorService(); const input1 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource1-openside')); const input2 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-openside')); @@ -509,15 +483,7 @@ suite('EditorService', () => { }); test('active editor change / visible editor change events', async function () { - const partInstantiator = workbenchInstantiationService(); - - const part = partInstantiator.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); - - const service: EditorServiceImpl = testInstantiationService.createInstance(EditorService); + const [part, service, testInstantiationService] = createEditorService(); let input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); let otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-active')); @@ -737,15 +703,7 @@ suite('EditorService', () => { }); test('two active editor change events when opening editor to the side', async function () { - const partInstantiator = workbenchInstantiationService(); - - const part = partInstantiator.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); - - const service: EditorServiceImpl = testInstantiationService.createInstance(EditorService); + const [part, service, testInstantiationService] = createEditorService(); let input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); @@ -782,15 +740,7 @@ suite('EditorService', () => { }); test('activeTextEditorWidget / activeTextEditorMode', async () => { - const partInstantiator = workbenchInstantiationService(); - - const part = partInstantiator.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); - - const service: EditorServiceImpl = testInstantiationService.createInstance(EditorService); + const [part, service] = createEditorService(); await part.whenRestored; @@ -805,15 +755,7 @@ suite('EditorService', () => { }); test('openEditor returns NULL when opening fails or is inactive', async function () { - const partInstantiator = workbenchInstantiationService(); - - const part = partInstantiator.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); - - const service: EditorServiceImpl = testInstantiationService.createInstance(EditorService); + const [part, service, testInstantiationService] = createEditorService(); const input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); const otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-inactive')); @@ -835,15 +777,7 @@ suite('EditorService', () => { }); test('save, saveAll, revertAll', async function () { - const partInstantiator = workbenchInstantiationService(); - - const part = partInstantiator.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); - - const service: IEditorService = testInstantiationService.createInstance(EditorService); + const [part, service, testInstantiationService] = createEditorService(); const input1 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource1-openside')); input1.dirty = true; diff --git a/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts b/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts index 97bb769736a..1dae9361931 100644 --- a/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts @@ -106,9 +106,9 @@ suite('EditorsObserver', function () { disposables = []; }); - - test('basics (single group)', async () => { + async function createPart(): Promise { const instantiationService = workbenchInstantiationService(); + instantiationService.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); const part = instantiationService.createInstance(EditorPart); part.create(document.createElement('div')); @@ -116,8 +116,20 @@ suite('EditorsObserver', function () { await part.whenRestored; + return part; + } + + async function createEditorObserver(): Promise<[EditorPart, EditorsObserver]> { + const part = await createPart(); + const observer = new EditorsObserver(part, new TestStorageService()); + return [part, observer]; + } + + test('basics (single group)', async () => { + const [part, observer] = await createEditorObserver(); + let observerChangeListenerCalled = false; const listener = observer.onDidChange(() => { observerChangeListenerCalled = true; @@ -183,18 +195,10 @@ suite('EditorsObserver', function () { }); test('basics (multi group)', async () => { - const instantiationService = workbenchInstantiationService(); - - const part = instantiationService.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - await part.whenRestored; + const [part, observer] = await createEditorObserver(); const rootGroup = part.activeGroup; - const observer = new EditorsObserver(part, new TestStorageService()); - let currentEditorsMRU = observer.editors; assert.equal(currentEditorsMRU.length, 0); @@ -252,15 +256,7 @@ suite('EditorsObserver', function () { }); test('copy group', async () => { - const instantiationService = workbenchInstantiationService(); - - const part = instantiationService.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - await part.whenRestored; - - const observer = new EditorsObserver(part, new TestStorageService()); + const [part, observer] = await createEditorObserver(); const input1 = new EditorsObserverTestEditorInput(URI.parse('foo://bar1')); const input2 = new EditorsObserverTestEditorInput(URI.parse('foo://bar2')); @@ -303,14 +299,7 @@ suite('EditorsObserver', function () { }); test('initial editors are part of observer and state is persisted & restored (single group)', async () => { - const instantiationService = workbenchInstantiationService(); - instantiationService.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - - const part = instantiationService.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - await part.whenRestored; + const part = await createPart(); const rootGroup = part.activeGroup; @@ -353,13 +342,7 @@ suite('EditorsObserver', function () { }); test('initial editors are part of observer (multi group)', async () => { - const instantiationService = workbenchInstantiationService(); - - const part = instantiationService.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - await part.whenRestored; + const part = await createPart(); const rootGroup = part.activeGroup; @@ -404,14 +387,7 @@ suite('EditorsObserver', function () { }); test('observer does not restore editors that cannot be serialized', async () => { - const instantiationService = workbenchInstantiationService(); - instantiationService.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - - const part = instantiationService.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - - await part.whenRestored; + const part = await createPart(); const rootGroup = part.activeGroup; @@ -440,18 +416,9 @@ suite('EditorsObserver', function () { }); test('observer closes editors when limit reached (across all groups)', async () => { - const instantiationService = workbenchInstantiationService(); - - instantiationService.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - - const part = instantiationService.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - + const part = await createPart(); part.enforcePartOptions({ limit: { enabled: true, value: 3 } }); - await part.whenRestored; - const storage = new TestStorageService(); const observer = new EditorsObserver(part, storage); @@ -501,18 +468,9 @@ suite('EditorsObserver', function () { }); test('observer closes editors when limit reached (in group)', async () => { - const instantiationService = workbenchInstantiationService(); - - instantiationService.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - - const part = instantiationService.createInstance(EditorPart); - part.create(document.createElement('div')); - part.layout(400, 300); - + const part = await createPart(); part.enforcePartOptions({ limit: { enabled: true, value: 3, perEditorGroup: true } }); - await part.whenRestored; - const storage = new TestStorageService(); const observer = new EditorsObserver(part, storage); diff --git a/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts b/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts index 63481a2e162..4d60e10c853 100644 --- a/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts +++ b/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts @@ -41,10 +41,10 @@ export interface IFilesConfigurationService { readonly onAutoSaveConfigurationChange: Event; - getAutoSaveMode(): AutoSaveMode; - getAutoSaveConfiguration(): IAutoSaveConfiguration; + getAutoSaveMode(): AutoSaveMode; + toggleAutoSave(): Promise; //#endregion diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 0c0d2cad011..3c0ece2ce43 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -8,8 +8,8 @@ import { URI } from 'vs/base/common/uri'; import { Emitter, AsyncEmitter } from 'vs/base/common/event'; import * as platform from 'vs/base/common/platform'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, FileOperationWillRunEvent, FileOperationDidRunEvent, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; -import { SaveReason, IRevertOptions, IEncodingSupport } from 'vs/workbench/common/editor'; +import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, ITextFileEditorModelManager, ITextFileEditorModel, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, FileOperationWillRunEvent, FileOperationDidRunEvent, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { IRevertOptions, IEncodingSupport } from 'vs/workbench/common/editor'; import { ILifecycleService, ShutdownReason, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileService, FileOperationError, FileOperationResult, HotExitConfiguration, IFileStatWithMetadata, ICreateFileOptions, FileOperation } from 'vs/platform/files/common/files'; @@ -482,19 +482,6 @@ export abstract class AbstractTextFileService extends Disposable implements ITex //#region save/revert async save(resource: URI, options?: ITextFileSaveOptions): Promise { - - // Run a forced save if we detect the file is not dirty so that save participants can still run - if (options?.force && this.fileService.canHandleResource(resource) && !this.isDirty(resource)) { - const model = this._models.get(resource); - if (model) { - options.reason = SaveReason.EXPLICIT; - - await model.save(options); - - return !model.isDirty(); - } - } - return !(await this.saveAll([resource], options)).results.some(result => result.error); } @@ -502,21 +489,29 @@ export abstract class AbstractTextFileService extends Disposable implements ITex saveAll(resources: URI[], options?: ITextFileSaveOptions): Promise; saveAll(arg1?: boolean | URI[], options?: ITextFileSaveOptions): Promise { - // get all dirty - let toSave: URI[] = []; + // Extract the resources to save + let resourcesToSave: URI[] = []; if (Array.isArray(arg1)) { - toSave = this.getDirty(arg1); + // if specific resources are given, we consider even + // non-dirty ones if options.force is provided + if (options?.force) { + resourcesToSave = arg1; + } else { + resourcesToSave = this.getDirty(arg1); + } } else { - toSave = this.getDirty(); + // if no resources are given, we only consider dirty + // resources even if options.force is provided + resourcesToSave = this.getDirty(); } // split up between files and untitled const filesToSave: URI[] = []; const untitledToSave: URI[] = []; - toSave.forEach(resourceToSave => { + resourcesToSave.forEach(resourceToSave => { if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && resourceToSave.scheme === Schemas.untitled) { untitledToSave.push(resourceToSave); - } else { + } else if (this.fileService.canHandleResource(resourceToSave)) { filesToSave.push(resourceToSave); } }); @@ -630,23 +625,17 @@ export abstract class AbstractTextFileService extends Disposable implements ITex } private async doSaveAllFiles(resources?: URI[], options: ITextFileSaveOptions = Object.create(null)): Promise { - const dirtyFileModels = this.getDirtyFileModels(Array.isArray(resources) ? resources : undefined /* Save All */) - .filter(model => { - if ((model.hasState(ModelState.CONFLICT) || model.hasState(ModelState.ERROR)) && (options.reason === SaveReason.AUTO || options.reason === SaveReason.FOCUS_CHANGE || options.reason === SaveReason.WINDOW_CHANGE)) { - return false; // if model is in save conflict or error, do not save unless save reason is explicit or not provided at all - } - - return true; - }); + const fileModelsToSave = this.getFileModels(resources); const mapResourceToResult = new ResourceMap(); - dirtyFileModels.forEach(dirtyModel => { - mapResourceToResult.set(dirtyModel.resource, { - source: dirtyModel.resource - }); - }); + for (const fileModelToSave of fileModelsToSave) { + mapResourceToResult.set(fileModelToSave.resource, { source: fileModelToSave.resource }); + } - await Promise.all(dirtyFileModels.map(async model => { + // Save all in parallel + await Promise.all(fileModelsToSave.map(async model => { + + // Save with options await model.save(options); // If model is still dirty, mark the resulting operation as error diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index afd56f642e9..53f5f8a3738 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -8,19 +8,19 @@ import { Emitter } from 'vs/base/common/event'; import { guessMimeTypes } from 'vs/base/common/mime'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { URI } from 'vs/base/common/uri'; -import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types'; +import { assertIsDefined } from 'vs/base/common/types'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextFileService, ModelState, ITextFileEditorModel, ISaveErrorHandler, ISaveParticipant, StateChange, ITextFileStreamContent, ILoadOptions, LoadReason, IResolvedTextFileEditorModel, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { EncodingMode, IRevertOptions, SaveReason } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { IFileService, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType, IFileStatWithMetadata, ETAG_DISABLED, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; +import { IFileService, FileOperationError, FileOperationResult, FileChangesEvent, FileChangeType, IFileStatWithMetadata, ETAG_DISABLED, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { RunOnceScheduler, timeout } from 'vs/base/common/async'; +import { timeout } from 'vs/base/common/async'; import { ITextBufferFactory } from 'vs/editor/common/model'; import { hash } from 'vs/base/common/hash'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -60,9 +60,6 @@ type TelemetryData = { */ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFileEditorModel { - static DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = CONTENT_CHANGE_EVENT_BUFFER_DELAY; - static DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY = 100; - static WHITELIST_JSON = ['package.json', 'package-lock.json', 'tsconfig.json', 'jsconfig.json', 'bower.json', '.eslintrc.json', 'tslint.json', 'composer.json']; static WHITELIST_WORKSPACE_JSON = ['settings.json', 'extensions.json', 'tasks.json', 'launch.json']; @@ -72,11 +69,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private static saveParticipant: ISaveParticipant | null; static setSaveParticipant(handler: ISaveParticipant | null): void { TextFileEditorModel.saveParticipant = handler; } - private readonly _onDidContentChange = this._register(new Emitter()); - readonly onDidContentChange = this._onDidContentChange.event; + private readonly _onDidChangeContent = this._register(new Emitter()); + readonly onDidChangeContent = this._onDidChangeContent.event; - private readonly _onDidStateChange = this._register(new Emitter()); - readonly onDidStateChange = this._onDidStateChange.event; + private readonly _onDidChangeState = this._register(new Emitter()); + readonly onDidChangeState = this._onDidChangeState.event; private readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; @@ -94,9 +91,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private readonly saveSequentializer = new SaveSequentializer(); private lastSaveAttemptTime = 0; - private readonly contentChangeEventScheduler = this._register(new RunOnceScheduler(() => this._onDidContentChange.fire(StateChange.CONTENT_CHANGE), TextFileEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY)); - private readonly orphanedChangeEventScheduler = this._register(new RunOnceScheduler(() => this._onDidStateChange.fire(StateChange.ORPHANED_CHANGE), TextFileEditorModel.DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY)); - private dirty = false; private inConflictMode = false; private inOrphanMode = false; @@ -132,18 +126,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private registerListeners(): void { this._register(this.fileService.onFileChanges(e => this.onFileChanges(e))); this._register(this.filesConfigurationService.onFilesAssociationChange(e => this.onFilesAssociationChange())); - this._register(this.onDidStateChange(e => this.onStateChange(e))); - } - - private onStateChange(e: StateChange): void { - if (e === StateChange.REVERTED) { - - // Cancel any content change event promises as they are no longer valid. - this.contentChangeEventScheduler.cancel(); - - // Refire state change reverted events as content change events - this._onDidContentChange.fire(StateChange.REVERTED); - } } private async onFileChanges(e: FileChangesEvent): Promise { @@ -194,7 +176,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private setOrphaned(orphaned: boolean): void { if (this.inOrphanMode !== orphaned) { this.inOrphanMode = orphaned; - this.orphanedChangeEventScheduler.schedule(); + this._onDidChangeState.fire(StateChange.ORPHANED_CHANGE); } } @@ -262,7 +244,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Emit file change event - this._onDidStateChange.fire(StateChange.REVERTED); + this._onDidChangeState.fire(StateChange.REVERTED); // Emit dirty change event if (wasDirty) { @@ -419,7 +401,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (this.preferredEncoding) { this.updatePreferredEncoding(this.contentEncoding); // make sure to reflect the real encoding of the file (never out of sync) } else if (oldEncoding !== this.contentEncoding) { - this._onDidStateChange.fire(StateChange.ENCODING); + this._onDidChangeState.fire(StateChange.ENCODING); } // Update Existing Model @@ -522,20 +504,18 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Emit event if (wasDirty) { - this._onDidStateChange.fire(StateChange.REVERTED); + this._onDidChangeState.fire(StateChange.REVERTED); this._onDidChangeDirty.fire(); } + } else { + this.logService.trace('[text file model] onModelContentChanged() - model content changed and marked as dirty', this.resource.toString()); - return; + // Mark as dirty + this.doMakeDirty(); } - this.logService.trace('[text file model] onModelContentChanged() - model content changed and marked as dirty', this.resource.toString()); - - // Mark as dirty - this.doMakeDirty(); - - // Handle content change events - this.contentChangeEventScheduler.schedule(); + // Emit as event + this._onDidChangeContent.fire(); } makeDirty(): void { @@ -554,7 +534,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Emit as Event if we turned dirty if (!wasDirty) { - this._onDidStateChange.fire(StateChange.DIRTY); + this._onDidChangeState.fire(StateChange.DIRTY); this._onDidChangeDirty.fire(); } } @@ -564,19 +544,37 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return false; } + if (this.isReadonly()) { + this.logService.trace('[text file model] save() - ignoring request for readonly resource', this.resource.toString()); + + return false; // if model is readonly we do not attempt to save at all + } + + if ( + (this.hasState(ModelState.CONFLICT) || this.hasState(ModelState.ERROR)) && + (options.reason === SaveReason.AUTO || options.reason === SaveReason.FOCUS_CHANGE || options.reason === SaveReason.WINDOW_CHANGE) + ) { + this.logService.trace('[text file model] save() - ignoring auto save request for model that is in conflict or error', this.resource.toString()); + + return false; // if model is in save conflict or error, do not save unless save reason is explicit + } + this.logService.trace('[text file model] save() - enter', this.resource.toString()); - await this.doSave(this.versionId, options); + await this.doSave(options); + + this.logService.trace('[text file model] save() - exit', this.resource.toString()); return true; } - private doSave(versionId: number, options: ITextFileSaveOptions): Promise { - if (isUndefinedOrNull(options.reason)) { + private doSave(options: ITextFileSaveOptions): Promise { + if (typeof options.reason !== 'number') { options.reason = SaveReason.EXPLICIT; } - this.logService.trace(`[text file model] doSave(${versionId}) - enter with versionId ' + versionId`, this.resource.toString()); + let versionId = this.versionId; + this.logService.trace(`[text file model] doSave(${versionId}) - enter with versionId ${versionId}`, this.resource.toString()); // Lookup any running pending save for this versionId and return it if found // @@ -589,14 +587,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.saveSequentializer.pendingSave || Promise.resolve(); } - // Return early if not dirty (unless forced) or version changed meanwhile + // Return early if not dirty (unless forced) // - // Scenario A: user invoked save action even though the model is not dirty - // Scenario B: auto save was triggered for a certain change by the user but meanwhile the user changed - // the contents and the version for which auto save was started is no longer the latest. - // Thus we avoid spawning multiple auto saves and only take the latest. - // - if ((!options.force && !this.dirty) || versionId !== this.versionId) { + // Scenario: user invoked save action even though the model is not dirty + if (!options.force && !this.dirty) { this.logService.trace(`[text file model] doSave(${versionId}) - exit - because not dirty and/or versionId is different (this.isDirty: ${this.dirty}, this.versionId: ${this.versionId})`, this.resource.toString()); return Promise.resolve(); @@ -614,7 +608,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.logService.trace(`[text file model] doSave(${versionId}) - exit - because busy saving`, this.resource.toString()); // Register this as the next upcoming save and return - return this.saveSequentializer.setNext(() => this.doSave(this.versionId /* make sure to use latest version id here */, options)); + return this.saveSequentializer.setNext(() => this.doSave(options)); } // Push all edit operations to the undo stack so that the user has a chance to @@ -702,11 +696,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Updated resolved stat with updated stat this.updateLastResolvedFileStat(stat); - // Cancel any content change event promises as they are no longer valid - this.contentChangeEventScheduler.cancel(); - // Emit Events - this._onDidStateChange.fire(StateChange.SAVED); + this._onDidChangeState.fire(StateChange.SAVED); this._onDidChangeDirty.fire(); // Telemetry @@ -735,7 +726,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.onSaveError(error); // Emit as event - this._onDidStateChange.fire(StateChange.SAVE_ERROR); + this._onDidChangeState.fire(StateChange.SAVE_ERROR); })); })); } @@ -809,7 +800,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.updateLastResolvedFileStat(stat); // Emit File Saved Event - this._onDidStateChange.fire(StateChange.SAVED); + this._onDidChangeState.fire(StateChange.SAVED); }, error => onUnexpectedError(error) /* just log any error but do not notify the user since the file was not dirty */)); } @@ -957,7 +948,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.preferredEncoding = encoding; // Emit - this._onDidStateChange.fire(StateChange.ENCODING); + this._onDidChangeState.fire(StateChange.ENCODING); } private isNewEncoding(encoding: string | undefined): boolean { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index d7116508ca0..deb34c132fb 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -18,12 +18,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; export class TextFileEditorModelManager extends Disposable implements ITextFileEditorModelManager { - private readonly _onModelDisposed = this._register(new Emitter()); - readonly onModelDisposed = this._onModelDisposed.event; - - private readonly _onModelContentChanged = this._register(new Emitter()); - readonly onModelContentChanged = this._onModelContentChanged.event; - private readonly _onModelDirty = this._register(new Emitter()); readonly onModelDirty = this._onModelDirty.event; @@ -69,15 +63,6 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return this._onModelsSaved; } - private _onModelsReverted: Event> | undefined; - get onModelsReverted(): Event> { - if (!this._onModelsReverted) { - this._onModelsReverted = this.debounce(this.onModelReverted); - } - - return this._onModelsReverted; - } - private readonly mapResourceToDisposeListener = new ResourceMap(); private readonly mapResourceToStateChangeListener = new ResourceMap(); private readonly mapResourceToModelContentChangeListener = new ResourceMap(); @@ -183,7 +168,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE modelPromise = model.load(options); // Install state change listener - this.mapResourceToStateChangeListener.set(resource, model.onDidStateChange(state => { + this.mapResourceToStateChangeListener.set(resource, model.onDidChangeState(state => { const event = new TextFileModelChangeEvent(newModel, state); switch (state) { case StateChange.DIRTY: @@ -206,11 +191,6 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE break; } })); - - // Install model content change listener - this.mapResourceToModelContentChangeListener.set(resource, model.onDidContentChange(e => { - this._onModelContentChanged.fire(new TextFileModelChangeEvent(newModel, e)); - })); } // Store pending loads to avoid race conditions @@ -281,10 +261,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // store in cache but remove when model gets disposed this.mapResourceToModel.set(resource, model); - this.mapResourceToDisposeListener.set(resource, model.onDispose(() => { - this.remove(resource); - this._onModelDisposed.fire(resource); - })); + this.mapResourceToDisposeListener.set(resource, model.onDispose(() => this.remove(resource))); } remove(resource: URI): void { diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index a61a80c21d4..8a0439d45de 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -277,12 +277,10 @@ export const enum ModelState { export const enum StateChange { DIRTY, - SAVING, SAVE_ERROR, SAVED, REVERTED, ENCODING, - CONTENT_CHANGE, ORPHANED_CHANGE } @@ -379,20 +377,17 @@ export interface IModelLoadOrCreateOptions { export interface ITextFileEditorModelManager { - readonly onModelDisposed: Event; - readonly onModelContentChanged: Event; readonly onModelEncodingChanged: Event; + readonly onModelOrphanedChanged: Event; readonly onModelDirty: Event; readonly onModelSaveError: Event; readonly onModelSaved: Event; readonly onModelReverted: Event; - readonly onModelOrphanedChanged: Event; readonly onModelsDirty: Event; readonly onModelsSaveError: Event; readonly onModelsSaved: Event; - readonly onModelsReverted: Event; get(resource: URI): ITextFileEditorModel | undefined; @@ -430,8 +425,7 @@ export interface ILoadOptions { export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport, IModeSupport, IWorkingCopy { - readonly onDidContentChange: Event; - readonly onDidStateChange: Event; + readonly onDidChangeState: Event; hasState(state: ModelState): boolean; diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index 3d8c90ba250..5d0f0212cad 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -52,6 +52,34 @@ suite('Files - TextFileEditorModel', () => { accessor.fileService.setContent(content); }); + test('basic events', async function () { + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + + await model.load(); + + let onDidChangeContentCounter = 0; + model.onDidChangeContent(() => onDidChangeContentCounter++); + + let onDidChangeDirtyCounter = 0; + model.onDidChangeDirty(() => onDidChangeDirtyCounter++); + + model.textEditorModel?.setValue('bar'); + + assert.equal(onDidChangeContentCounter, 1); + assert.equal(onDidChangeDirtyCounter, 1); + + model.textEditorModel?.setValue('foo'); + + assert.equal(onDidChangeContentCounter, 2); + assert.equal(onDidChangeDirtyCounter, 1); + + await model.revert(); + + assert.equal(onDidChangeDirtyCounter, 2); + + model.dispose(); + }); + test('save', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); @@ -67,7 +95,7 @@ suite('Files - TextFileEditorModel', () => { assert.equal(accessor.workingCopyService.isDirty(model.resource), true); let savedEvent = false; - model.onDidStateChange(e => { + model.onDidChangeState(e => { if (e === StateChange.SAVED) { savedEvent = true; } @@ -104,7 +132,7 @@ suite('Files - TextFileEditorModel', () => { await model.load(); let savedEvent = false; - model.onDidStateChange(e => { + model.onDidChangeState(e => { if (e === StateChange.SAVED) { savedEvent = true; } @@ -178,7 +206,7 @@ suite('Files - TextFileEditorModel', () => { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8', undefined); assert.ok(model.hasState(ModelState.SAVED)); - model.onDidStateChange(e => { + model.onDidChangeState(e => { assert.ok(e !== StateChange.DIRTY && e !== StateChange.SAVED); }); @@ -206,7 +234,7 @@ suite('Files - TextFileEditorModel', () => { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - model.onDidStateChange(e => { + model.onDidChangeState(e => { if (e === StateChange.REVERTED) { eventCounter++; } @@ -243,7 +271,7 @@ suite('Files - TextFileEditorModel', () => { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - model.onDidStateChange(e => { + model.onDidChangeState(e => { if (e === StateChange.REVERTED) { eventCounter++; } @@ -303,7 +331,7 @@ suite('Files - TextFileEditorModel', () => { await model.revert({ soft: true }); assert.ok(!model.isDirty()); - model.onDidStateChange(e => { + model.onDidChangeState(e => { if (e === StateChange.DIRTY) { eventCounter++; } @@ -388,7 +416,7 @@ suite('Files - TextFileEditorModel', () => { let eventCounter = 0; const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - model.onDidStateChange(e => { + model.onDidChangeState(e => { if (e === StateChange.SAVED) { assert.equal(snapshotToString(model.createSnapshot()!), 'bar'); assert.ok(!model.isDirty()); diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts index f4db513644f..4145c7f6f8f 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -136,9 +136,6 @@ suite('Files - TextFileEditorModelManager', () => { }); test('events', async function () { - TextFileEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = 0; - TextFileEditorModel.DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY = 0; - const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); const resource1 = toResource.call(this, '/path/index.txt'); @@ -148,8 +145,6 @@ suite('Files - TextFileEditorModelManager', () => { let revertedCounter = 0; let savedCounter = 0; let encodingCounter = 0; - let disposeCounter = 0; - let contentCounter = 0; manager.onModelDirty(e => { if (e.resource.toString() === resource1.toString()) { @@ -175,16 +170,6 @@ suite('Files - TextFileEditorModelManager', () => { } }); - manager.onModelContentChanged(e => { - if (e.resource.toString() === resource1.toString()) { - contentCounter++; - } - }); - - manager.onModelDisposed(e => { - disposeCounter++; - }); - const model1 = await manager.loadOrCreate(resource1, { encoding: 'utf8' }); accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.DELETED }])); accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.ADDED }])); @@ -199,7 +184,6 @@ suite('Files - TextFileEditorModelManager', () => { await model1.save(); model1.dispose(); model2.dispose(); - assert.equal(disposeCounter, 2); await model1.revert(); assert.equal(dirtyCounter, 2); @@ -207,8 +191,6 @@ suite('Files - TextFileEditorModelManager', () => { assert.equal(savedCounter, 1); assert.equal(encodingCounter, 2); - await timeout(10); - assert.equal(contentCounter, 2); model1.dispose(); model2.dispose(); assert.ok(!accessor.modelService.getModel(resource1)); @@ -222,21 +204,13 @@ suite('Files - TextFileEditorModelManager', () => { const resource2 = toResource.call(this, '/path/other.txt'); let dirtyCounter = 0; - let revertedCounter = 0; let savedCounter = 0; - TextFileEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = 0; - manager.onModelsDirty(e => { dirtyCounter += e.length; assert.equal(e[0].resource.toString(), resource1.toString()); }); - manager.onModelsReverted(e => { - revertedCounter += e.length; - assert.equal(e[0].resource.toString(), resource1.toString()); - }); - manager.onModelsSaved(e => { savedCounter += e.length; assert.equal(e[0].resource.toString(), resource1.toString()); @@ -257,7 +231,6 @@ suite('Files - TextFileEditorModelManager', () => { await model1.revert(); await timeout(20); assert.equal(dirtyCounter, 2); - assert.equal(revertedCounter, 1); assert.equal(savedCounter, 1); model1.dispose(); model2.dispose(); diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts index 9131aa8def5..42c6848278a 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts @@ -30,16 +30,6 @@ export interface IUntitledTextEditorService { _serviceBrand: undefined; - /** - * Events for when untitled text editors are created. - */ - readonly onDidCreate: Event; - - /** - * Events for when untitled text editors content changes (e.g. any keystroke). - */ - readonly onDidChangeContent: Event; - /** * Events for when untitled text editors change (e.g. getting dirty, saved or reverted). */ @@ -113,12 +103,6 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe private mapResourceToInput = new ResourceMap(); private mapResourceToAssociatedFilePath = new ResourceMap(); - private readonly _onDidCreate = this._register(new Emitter()); - readonly onDidCreate = this._onDidCreate.event; - - private readonly _onDidChangeContent = this._register(new Emitter()); - readonly onDidChangeContent = this._onDidChangeContent.event; - private readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; @@ -246,7 +230,6 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe const input = this.instantiationService.createInstance(UntitledTextEditorInput, untitledResource, !!hasAssociatedFilePath, mode, initialValue, encoding); - const contentListener = input.onDidModelChangeContent(() => this._onDidChangeContent.fire(untitledResource)); const dirtyListener = input.onDidChangeDirty(() => this._onDidChangeDirty.fire(untitledResource)); const encodingListener = input.onDidModelChangeEncoding(() => this._onDidChangeEncoding.fire(untitledResource)); const disposeListener = input.onDispose(() => this._onDidDisposeModel.fire(untitledResource)); @@ -256,7 +239,6 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe onceDispose(() => { this.mapResourceToInput.delete(input.getResource()); this.mapResourceToAssociatedFilePath.delete(input.getResource()); - contentListener.dispose(); dirtyListener.dispose(); encodingListener.dispose(); disposeListener.dispose(); @@ -265,9 +247,6 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe // Add to cache this.mapResourceToInput.set(untitledResource, input); - // Signal new untitled as event - this._onDidCreate.fire(untitledResource); - return input; } diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index e79533e7333..4c12fbb093f 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -28,19 +28,28 @@ export interface IWorkingCopy { readonly capabilities: WorkingCopyCapabilities; - //#region Dirty Tracking + //#region Events readonly onDidChangeDirty: Event; + readonly onDidChangeContent: Event; + + //#endregion + + + //#region Dirty Tracking + isDirty(): boolean; //#endregion - //#region Save + //#region Save / Backup save(options?: ISaveOptions): Promise; + backup(): Promise; + //#endregion } @@ -51,10 +60,21 @@ export interface IWorkingCopyService { _serviceBrand: undefined; - //#region Dirty Tracking + //#region Events + + readonly onDidRegister: Event; + + readonly onDidUnregister: Event; readonly onDidChangeDirty: Event; + readonly onDidChangeContent: Event; + + //#endregion + + + //#region Dirty Tracking + readonly dirtyCount: number; readonly hasDirty: boolean; @@ -77,11 +97,25 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic _serviceBrand: undefined; - //#region Dirty Tracking + //#region Events + + private readonly _onDidRegister = this._register(new Emitter()); + readonly onDidRegister = this._onDidRegister.event; + + private readonly _onDidUnregister = this._register(new Emitter()); + readonly onDidUnregister = this._onDidUnregister.event; private readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; + private readonly _onDidChangeContent = this._register(new Emitter()); + readonly onDidChangeContent = this._onDidChangeContent.event; + + //#endregion + + + //#region Dirty Tracking + isDirty(resource: URI): boolean { const workingCopies = this.mapResourceToWorkingCopy.get(resource.toString()); if (workingCopies) { @@ -141,8 +175,12 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic this._workingCopies.add(workingCopy); - // Dirty Events + // Wire in Events + disposables.add(workingCopy.onDidChangeContent(() => this._onDidChangeContent.fire(workingCopy))); disposables.add(workingCopy.onDidChangeDirty(() => this._onDidChangeDirty.fire(workingCopy))); + + // Send some initial events + this._onDidRegister.fire(workingCopy); if (workingCopy.isDirty()) { this._onDidChangeDirty.fire(workingCopy); } @@ -150,6 +188,9 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic return toDisposable(() => { this.unregisterWorkingCopy(workingCopy); dispose(disposables); + + // Signal as event + this._onDidUnregister.fire(workingCopy); }); } diff --git a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts index ee32a833262..908c24a2251 100644 --- a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts @@ -18,6 +18,9 @@ suite('WorkingCopyService', () => { private readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; + private readonly _onDidChangeContent = this._register(new Emitter()); + readonly onDidChangeContent = this._onDidChangeContent.event; + private readonly _onDispose = this._register(new Emitter()); readonly onDispose = this._onDispose.event; @@ -38,6 +41,10 @@ suite('WorkingCopyService', () => { } } + setContent(content: string): void { + this._onDidChangeContent.fire(); + } + isDirty(): boolean { return this.dirty; } @@ -46,6 +53,8 @@ suite('WorkingCopyService', () => { return true; } + async backup(): Promise { } + dispose(): void { this._onDispose.fire(); @@ -59,6 +68,15 @@ suite('WorkingCopyService', () => { const onDidChangeDirty: IWorkingCopy[] = []; service.onDidChangeDirty(copy => onDidChangeDirty.push(copy)); + const onDidChangeContent: IWorkingCopy[] = []; + service.onDidChangeContent(copy => onDidChangeContent.push(copy)); + + const onDidRegister: IWorkingCopy[] = []; + service.onDidRegister(copy => onDidRegister.push(copy)); + + const onDidUnregister: IWorkingCopy[] = []; + service.onDidUnregister(copy => onDidUnregister.push(copy)); + assert.equal(service.hasDirty, false); assert.equal(service.dirtyCount, 0); assert.equal(service.workingCopies.length, 0); @@ -70,6 +88,9 @@ suite('WorkingCopyService', () => { const unregister1 = service.registerWorkingCopy(copy1); assert.equal(service.workingCopies.length, 1); + assert.equal(service.workingCopies[0], copy1); + assert.equal(onDidRegister.length, 1); + assert.equal(onDidRegister[0], copy1); assert.equal(service.dirtyCount, 0); assert.equal(service.isDirty(resource1), false); assert.equal(service.hasDirty, false); @@ -82,6 +103,11 @@ suite('WorkingCopyService', () => { assert.equal(onDidChangeDirty.length, 1); assert.equal(onDidChangeDirty[0], copy1); + copy1.setContent('foo'); + + assert.equal(onDidChangeContent.length, 1); + assert.equal(onDidChangeContent[0], copy1); + copy1.setDirty(false); assert.equal(service.dirtyCount, 0); @@ -92,6 +118,8 @@ suite('WorkingCopyService', () => { unregister1.dispose(); + assert.equal(onDidUnregister.length, 1); + assert.equal(onDidUnregister[0], copy1); assert.equal(service.workingCopies.length, 0); // resource 2 @@ -99,6 +127,8 @@ suite('WorkingCopyService', () => { const copy2 = new TestWorkingCopy(resource2, true); const unregister2 = service.registerWorkingCopy(copy2); + assert.equal(onDidRegister.length, 2); + assert.equal(onDidRegister[1], copy2); assert.equal(service.dirtyCount, 1); assert.equal(service.isDirty(resource2), true); assert.equal(service.hasDirty, true); @@ -106,7 +136,15 @@ suite('WorkingCopyService', () => { assert.equal(onDidChangeDirty.length, 3); assert.equal(onDidChangeDirty[2], copy2); + copy2.setContent('foo'); + + assert.equal(onDidChangeContent.length, 2); + assert.equal(onDidChangeContent[1], copy2); + unregister2.dispose(); + + assert.equal(onDidUnregister.length, 2); + assert.equal(onDidUnregister[1], copy2); assert.equal(service.dirtyCount, 0); assert.equal(service.hasDirty, false); assert.equal(onDidChangeDirty.length, 4); diff --git a/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts b/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts index c9ca35dc144..ca9bba1e27b 100644 --- a/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts +++ b/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts @@ -10,11 +10,9 @@ import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbe import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { workbenchInstantiationService, TestEditorService } from 'vs/workbench/test/workbenchTestServices'; -import { UntitledTextEditorModel } from 'vs/workbench/common/editor/untitledTextEditorModel'; import { IModeService } from 'vs/editor/common/services/modeService'; import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput'; -import { timeout } from 'vs/base/common/async'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; @@ -56,20 +54,11 @@ suite('Workbench untitled text editors', () => { assert.equal(service.getAll().length, 0); - let createdResources: URI[] = []; - const createListener = service.onDidCreate(resource => { - createdResources.push(resource); - }); - const input1 = service.createOrGet(); assert.equal(input1, service.createOrGet(input1.getResource())); assert.ok(service.exists(input1.getResource())); assert.ok(!service.exists(URI.file('testing'))); - assert.equal(createdResources.length, 1); - assert.equal(createdResources[0].toString(), input1.getResource()); - - createListener.dispose(); const input2 = service.createOrGet(); @@ -122,17 +111,18 @@ suite('Workbench untitled text editors', () => { model.textEditorModel.setValue('foo bar'); }); - test('Untitled with associated resource', () => { + test('Untitled with associated resource is dirty', () => { const service = accessor.untitledTextEditorService; const file = URI.file(join('C:\\', '/foo/file.txt')); const untitled = service.createOrGet(file); assert.ok(service.hasAssociatedFilePath(untitled.getResource())); + assert.equal(untitled.isDirty(), true); untitled.dispose(); }); - test('Untitled no longer dirty when content gets empty', async () => { + test('Untitled no longer dirty when content gets empty (not with associated resource)', async () => { const service = accessor.untitledTextEditorService; const workingCopyService = accessor.workingCopyService; const input = service.createOrGet(); @@ -203,21 +193,23 @@ suite('Workbench untitled text editors', () => { test('Untitled with initial content is dirty', async () => { const service = accessor.untitledTextEditorService; - const input = service.createOrGet(undefined, undefined, 'Hello World'); const workingCopyService = accessor.workingCopyService; + const untitled = service.createOrGet(undefined, undefined, 'Hello World'); + assert.equal(untitled.isDirty(), true); + let onDidChangeDirty: IWorkingCopy | undefined = undefined; const listener = workingCopyService.onDidChangeDirty(copy => { onDidChangeDirty = copy; }); // dirty - const model = await input.resolve(); + const model = await untitled.resolve(); assert.ok(model.isDirty()); assert.equal(workingCopyService.dirtyCount, 1); assert.equal(onDidChangeDirty, model); - input.dispose(); + untitled.dispose(); listener.dispose(); }); @@ -309,41 +301,47 @@ suite('Workbench untitled text editors', () => { input.dispose(); }); - test('onDidChangeContent event', async () => { + test('onDidChangeContent event', async function () { const service = accessor.untitledTextEditorService; const input = service.createOrGet(); - UntitledTextEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = 0; - let counter = 0; - service.onDidChangeContent(r => { - counter++; - assert.equal(r.toString(), input.getResource().toString()); - }); - const model = await input.resolve(); - model.textEditorModel.setValue('foo'); - assert.equal(counter, 0, 'Dirty model should not trigger event immediately'); + model.onDidChangeContent(() => counter++); + + model.textEditorModel.setValue('foo'); - await timeout(3); assert.equal(counter, 1, 'Dirty model should trigger event'); model.textEditorModel.setValue('bar'); - await timeout(3); assert.equal(counter, 2, 'Content change when dirty should trigger event'); model.textEditorModel.setValue(''); - await timeout(3); assert.equal(counter, 3, 'Manual revert should trigger event'); model.textEditorModel.setValue('foo'); - await timeout(3); assert.equal(counter, 4, 'Dirty model should trigger event'); - model.revert(); - await timeout(3); - assert.equal(counter, 5, 'Revert should trigger event'); + input.dispose(); + }); + + test('onDidChangeDirty event', async function () { + const service = accessor.untitledTextEditorService; + const input = service.createOrGet(); + + let counter = 0; + + const model = await input.resolve(); + model.onDidChangeDirty(() => counter++); + + model.textEditorModel.setValue('foo'); + + assert.equal(counter, 1, 'Dirty model should trigger event'); + model.textEditorModel.setValue('bar'); + + assert.equal(counter, 1, 'Another change does not fire event'); + input.dispose(); }); From 02cce060b6f653c0b444533c3873624ad00fc41b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 12 Jan 2020 12:22:05 +0100 Subject: [PATCH 165/843] preventSaveConflicts - allow as language setting --- .../contrib/files/browser/files.contribution.ts | 2 +- .../common/filesConfigurationService.ts | 11 +++++++---- .../services/textfile/common/textFileEditorModel.ts | 4 +++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 39744d2ae4a..aedef12163e 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -326,7 +326,7 @@ configurationRegistry.registerConfiguration({ 'type': 'boolean', 'description': nls.localize('files.preventSaveConflicts', "When enabled, will prevent to save a file that has been changed since it was last edited. Instead, a diff editor is provided to compare the changes and accept or revert them. This setting should only be disabled if you frequently encounter save conflict errors and may result in data loss if used without caution."), 'default': true, - 'scope': ConfigurationScope.RESOURCE + 'scope': ConfigurationScope.RESOURCE_LANGUAGE }, 'files.simpleDialog.enable': { 'type': 'boolean', diff --git a/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts b/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts index 4d60e10c853..0df4d59fa9b 100644 --- a/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts +++ b/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts @@ -14,6 +14,7 @@ import { isUndefinedOrNull } from 'vs/base/common/types'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { equals } from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; +import { isWeb } from 'vs/base/common/platform'; export const AutoSaveAfterShortDelayContext = new RawContextKey('autoSaveAfterShortDelayContext', false); @@ -55,13 +56,15 @@ export interface IFilesConfigurationService { readonly hotExitConfiguration: string | undefined; - preventSaveConflicts(resource: URI): boolean; + preventSaveConflicts(resource: URI, language: string): boolean; } export class FilesConfigurationService extends Disposable implements IFilesConfigurationService { _serviceBrand: undefined; + private static DEFAULT_AUTO_SAVE_MODE = isWeb ? AutoSaveConfiguration.AFTER_DELAY : AutoSaveConfiguration.OFF; + private readonly _onAutoSaveConfigurationChange = this._register(new Emitter()); readonly onAutoSaveConfigurationChange = this._onAutoSaveConfigurationChange.event; @@ -110,7 +113,7 @@ export class FilesConfigurationService extends Disposable implements IFilesConfi protected onFilesConfigurationChange(configuration: IFilesConfiguration): void { // Auto Save - const autoSaveMode = configuration?.files?.autoSave || AutoSaveConfiguration.OFF; + const autoSaveMode = configuration?.files?.autoSave || FilesConfigurationService.DEFAULT_AUTO_SAVE_MODE; switch (autoSaveMode) { case AutoSaveConfiguration.AFTER_DELAY: this.configuredAutoSaveDelay = configuration?.files?.autoSaveDelay; @@ -207,8 +210,8 @@ export class FilesConfigurationService extends Disposable implements IFilesConfi return this.currentHotExitConfig; } - preventSaveConflicts(resource: URI): boolean { - return this.configurationService.getValue('files.preventSaveConflicts', { resource }); + preventSaveConflicts(resource: URI, language: string): boolean { + return this.configurationService.getValue('files.preventSaveConflicts', { resource, overrideIdentifier: language }); } } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 53f5f8a3738..19ade6f51be 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -680,7 +680,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil overwriteEncoding: options.overwriteEncoding, mtime: lastResolvedFileStat.mtime, encoding: this.getEncoding(), - etag: (options.ignoreModifiedSince || !this.filesConfigurationService.preventSaveConflicts(lastResolvedFileStat.resource)) ? ETAG_DISABLED : lastResolvedFileStat.etag, + etag: (options.ignoreModifiedSince || !this.filesConfigurationService.preventSaveConflicts(lastResolvedFileStat.resource, this.getMode())) ? ETAG_DISABLED : lastResolvedFileStat.etag, writeElevated: options.writeElevated }).then(stat => { this.logService.trace(`[text file model] doSave(${versionId}) - after write()`, this.resource.toString()); @@ -891,6 +891,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } + getMode(this: IResolvedTextFileEditorModel): string; + getMode(): string | undefined; getMode(): string | undefined { if (this.textEditorModel) { return this.textEditorModel.getModeId(); From e15873eae8d398394dcb6bf85c22447e4283dc23 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 12 Jan 2020 18:29:17 +0100 Subject: [PATCH 166/843] #85216 Add configure sync command --- .../contrib/userDataSync/browser/userDataSync.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 07264e336bd..9c9dd463da7 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -552,5 +552,15 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }; CommandsRegistry.registerCommand(signOutMenuItem.command.id, () => this.signOut()); MenuRegistry.appendMenuItem(MenuId.CommandPalette, signOutMenuItem); + + const configureSyncCommandId = 'workbench.userData.actions.configureSync'; + CommandsRegistry.registerCommand(configureSyncCommandId, () => this.configureSyncOptions()); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: configureSyncCommandId, + title: localize('configure sync', "Sync: Configure") + }, + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`)), + }); } } From 478f9019ce4c33e3ceb3c0b924607ffd8e953270 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 12 Jan 2020 18:38:35 +0100 Subject: [PATCH 167/843] #85216 Add confirmation while turning off --- .../contrib/userDataSync/browser/userDataSync.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 9c9dd463da7..666725b9922 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -278,10 +278,10 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo let detail: string, primaryButton: string; if (this.authTokenService.status === AuthTokenStatus.SignedIn) { detail = this.getTurnOnDetailString(); - primaryButton = localize('turn on sync', "Turn on Sync"); + primaryButton = localize('turn on', "Turn on"); } else { detail = this.getSignInAndTurnOnDetailString(); - primaryButton = localize('sign in and turn on sync', "Sign in & Turn on Sync"); + primaryButton = localize('sign in and turn on sync', "Sign in & Turn on"); } const result = await this.dialogService.show(Severity.Info, message, [primaryButton, localize('cancel', "Cancel"), localize('configure', "Configure")], { detail, cancelId: 1 }); switch (result.choice) { @@ -341,7 +341,15 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } private async turnOff(): Promise { - await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, false); + const result = await this.dialogService.confirm({ + type: 'info', + message: localize('turn off sync confirmation', "Turn off Sync"), + detail: localize('turn off sync detail', "Your settings, keybindings, extensions and more will no longer be synced."), + primaryButton: localize('turn off', "Turn off") + }); + if (result.confirmed) { + await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, false); + } } private async signIn(): Promise { From 22e4d75bd07b1ec7362c69da9dda7667eae95374 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 12 Jan 2020 20:45:44 +0100 Subject: [PATCH 168/843] Fix #86096 --- .../userDataSync/common/userDataSync.ts | 7 ++-- .../common/userDataSyncService.ts | 35 ++++++++++++------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 6ee18e5c756..cb042269bc3 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -167,9 +167,10 @@ export interface IGlobalState { } export const enum SyncSource { - Settings = 1, - Keybindings, - Extensions + Settings = 'Settings', + Keybindings = 'Keybindings', + Extensions = 'Extensions', + UIState = 'UI State' } export const enum SyncStatus { diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 1bf9f49040d..7d13861b945 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource, ISettingsSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource, ISettingsSyncService, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; @@ -13,6 +13,7 @@ import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; import { KeybindingsSynchroniser } from 'vs/platform/userDataSync/common/keybindingsSync'; import { GlobalStateSynchroniser } from 'vs/platform/userDataSync/common/globalStateSync'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; export class UserDataSyncService extends Disposable implements IUserDataSyncService { @@ -39,6 +40,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ @IInstantiationService private readonly instantiationService: IInstantiationService, @IAuthTokenService private readonly authTokenService: IAuthTokenService, @ISettingsSyncService private readonly settingsSynchroniser: ISettingsSyncService, + @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, ) { super(); this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser)); @@ -62,8 +64,12 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ throw new Error('Not Authenticated. Please sign in to start sync.'); } for (const synchroniser of this.synchronisers) { - if (!await synchroniser.sync(_continue)) { - return false; + try { + if (!await synchroniser.sync(_continue)) { + return false; + } + } catch (e) { + this.logService.error(`${this.getSyncSource(synchroniser)}: ${toErrorMessage(e)}`); } } return true; @@ -108,15 +114,20 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } private computeConflictsSource(): SyncSource | null { - const source = this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts)[0]; - if (source) { - if (source instanceof SettingsSynchroniser) { - return SyncSource.Settings; - } - if (source instanceof KeybindingsSynchroniser) { - return SyncSource.Keybindings; - } + const synchroniser = this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts)[0]; + return synchroniser ? this.getSyncSource(synchroniser) : null; + } + + private getSyncSource(synchroniser: ISynchroniser): SyncSource { + if (synchroniser instanceof SettingsSynchroniser) { + return SyncSource.Settings; } - return null; + if (synchroniser instanceof KeybindingsSynchroniser) { + return SyncSource.Keybindings; + } + if (synchroniser instanceof ExtensionsSynchroniser) { + return SyncSource.Extensions; + } + return SyncSource.UIState; } } From 528202e34f9b98ba92d07a00d743847b9bed7b12 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 13 Jan 2020 08:37:02 +0100 Subject: [PATCH 169/843] #85216 methods to know if synced before --- .../userDataSync/common/extensionsSync.ts | 10 +++++++ .../userDataSync/common/globalStateSync.ts | 10 +++++++ .../userDataSync/common/keybindingsSync.ts | 10 +++++++ .../userDataSync/common/settingsSync.ts | 10 +++++++ .../userDataSync/common/userDataSync.ts | 2 ++ .../userDataSync/common/userDataSyncIpc.ts | 4 +++ .../common/userDataSyncService.ts | 30 +++++++++++++++++++ .../electron-browser/settingsSyncService.ts | 8 +++++ .../electron-browser/userDataSyncService.ts | 8 +++++ 9 files changed, 92 insertions(+) diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 43d86e6f26e..98beb932ced 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -108,6 +108,16 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser stop(): void { } + async hasPreviouslySynced(): Promise { + const lastSyncData = await this.getLastSyncUserData(); + return !!lastSyncData; + } + + async hasRemote(): Promise { + const remoteUserData = await this.userDataSyncStoreService.read(ExtensionsSynchroniser.EXTERNAL_USER_DATA_EXTENSIONS_KEY, null); + return remoteUserData.content !== null; + } + removeExtension(identifier: IExtensionIdentifier): Promise { return this.replaceQueue.queue(async () => { const remoteData = await this.userDataSyncStoreService.read(ExtensionsSynchroniser.EXTERNAL_USER_DATA_EXTENSIONS_KEY, null); diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index ff5ce0d5ae9..5677baaf1f1 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -85,6 +85,16 @@ export class GlobalStateSynchroniser extends Disposable implements ISynchroniser stop(): void { } + async hasPreviouslySynced(): Promise { + const lastSyncData = await this.getLastSyncUserData(); + return !!lastSyncData; + } + + async hasRemote(): Promise { + const remoteUserData = await this.userDataSyncStoreService.read(GlobalStateSynchroniser.EXTERNAL_USER_DATA_GLOBAL_STATE_KEY, null); + return remoteUserData.content !== null; + } + private async doSync(): Promise { const lastSyncData = await this.getLastSyncUserData(); const lastSyncGlobalState = lastSyncData && lastSyncData.content ? JSON.parse(lastSyncData.content) : null; diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 4735db86782..1b92064f972 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -128,6 +128,16 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser this.setStatus(SyncStatus.Idle); } + async hasPreviouslySynced(): Promise { + const lastSyncData = await this.getLastSyncUserData(); + return !!lastSyncData; + } + + async hasRemote(): Promise { + const remoteUserData = await this.userDataSyncStoreService.read(KeybindingsSynchroniser.EXTERNAL_USER_DATA_KEYBINDINGS_KEY, null); + return remoteUserData.content !== null; + } + private async continueSync(): Promise { if (this.status !== SyncStatus.HasConflicts) { return false; diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 5f21b275d5b..89dc8c0ae81 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -117,6 +117,16 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer this.setStatus(SyncStatus.Idle); } + async hasPreviouslySynced(): Promise { + const lastSyncData = await this.getLastSyncUserData(); + return !!lastSyncData; + } + + async hasRemote(): Promise { + const remoteUserData = await this.userDataSyncStoreService.read(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY, null); + return remoteUserData.content !== null; + } + async resolveConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise { if (this.status === SyncStatus.HasConflicts) { this.syncPreviewResultPromise!.cancel(); diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index cb042269bc3..3558afcb8e8 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -186,6 +186,8 @@ export interface ISynchroniser { readonly onDidChangeLocal: Event; sync(_continue?: boolean): Promise; stop(): void; + hasPreviouslySynced(): Promise + hasRemote(): Promise; } export const IUserDataSyncService = createDecorator('IUserDataSyncService'); diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index bcd53cae1b7..05f4d99128f 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -29,6 +29,8 @@ export class UserDataSyncChannel implements IServerChannel { case 'getConflictsSource': return Promise.resolve(this.service.conflictsSource); case 'removeExtension': return this.service.removeExtension(args[0]); case 'stop': this.service.stop(); return Promise.resolve(); + case 'hasPreviouslySynced': return this.service.hasPreviouslySynced(); + case 'hasRemote': return this.service.hasRemote(); } throw new Error('Invalid call'); } @@ -53,6 +55,8 @@ export class SettingsSyncChannel implements IServerChannel { case '_getInitialStatus': return Promise.resolve(this.service.status); case '_getInitialConflicts': return Promise.resolve(this.service.conflicts); case 'stop': this.service.stop(); return Promise.resolve(); + case 'hasPreviouslySynced': return this.service.hasPreviouslySynced(); + case 'hasRemote': return this.service.hasRemote(); case 'resolveConflicts': return this.service.resolveConflicts(args[0]); } throw new Error('Invalid call'); diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 7d13861b945..5a23f9d01c4 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -84,6 +84,36 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } } + async hasPreviouslySynced(): Promise { + if (!this.userDataSyncStoreService.userDataSyncStore) { + throw new Error('Not enabled'); + } + if (this.authTokenService.status === AuthTokenStatus.SignedOut) { + throw new Error('Not Authenticated. Please sign in to start sync.'); + } + for (const synchroniser of this.synchronisers) { + if (await synchroniser.hasPreviouslySynced()) { + return true; + } + } + return false; + } + + async hasRemote(): Promise { + if (!this.userDataSyncStoreService.userDataSyncStore) { + throw new Error('Not enabled'); + } + if (this.authTokenService.status === AuthTokenStatus.SignedOut) { + throw new Error('Not Authenticated. Please sign in to start sync.'); + } + for (const synchroniser of this.synchronisers) { + if (await synchroniser.hasRemote()) { + return true; + } + } + return false; + } + removeExtension(identifier: IExtensionIdentifier): Promise { return this.extensionsSynchroniser.removeExtension(identifier); } diff --git a/src/vs/workbench/services/userDataSync/electron-browser/settingsSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/settingsSyncService.ts index 0a2adc53f3a..51c3a999ff6 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/settingsSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/settingsSyncService.ts @@ -53,6 +53,14 @@ export class SettingsSyncService extends Disposable implements ISettingsSyncServ this.channel.call('stop'); } + hasPreviouslySynced(): Promise { + return this.channel.call('hasPreviouslySynced'); + } + + hasRemote(): Promise { + return this.channel.call('hasRemote'); + } + resolveConflicts(conflicts: { key: string, value: any | undefined }[]): Promise { return this.channel.call('resolveConflicts', [conflicts]); } diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts index 8a0bf37d888..fe09e70eec6 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts @@ -46,6 +46,14 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.channel.call('stop'); } + hasPreviouslySynced(): Promise { + return this.channel.call('hasPreviouslySynced'); + } + + hasRemote(): Promise { + return this.channel.call('hasRemote'); + } + removeExtension(identifier: IExtensionIdentifier): Promise { return this.channel.call('removeExtension', [identifier]); } From b84d08be70d1efe55d6097d3badcc03297a27635 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 Jan 2020 10:04:48 +0100 Subject: [PATCH 170/843] options - make sure text editor options are proper --- src/vs/platform/editor/common/editor.ts | 6 +- src/vs/workbench/common/editor.ts | 105 +++++++----------- .../test/common/editor/editorOptions.test.ts | 39 ------- 3 files changed, 45 insertions(+), 105 deletions(-) delete mode 100644 src/vs/workbench/test/common/editor/editorOptions.test.ts diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index 3185e8c9f96..fcbef52e0f3 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -220,15 +220,15 @@ export interface ITextEditorOptions extends IEditorOptions { /** * Text editor selection. */ - selection?: ITextEditorSelection; + readonly selection?: ITextEditorSelection; /** * Text editor view state. */ - viewState?: object; + readonly viewState?: object; /** * Option to scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport. */ - revealInCenterIfOutsideViewport?: boolean; + readonly revealInCenterIfOutsideViewport?: boolean; } diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 6a55d4bd697..21cbe96e150 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -5,11 +5,11 @@ import { Event, Emitter } from 'vs/base/common/event'; import { assign } from 'vs/base/common/objects'; -import { isUndefinedOrNull, withNullAsUndefined, assertIsDefined } from 'vs/base/common/types'; +import { withNullAsUndefined, assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IEditor as ICodeEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon'; -import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, IResourceInput, EditorActivation, EditorOpenContext } from 'vs/platform/editor/common/editor'; +import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, IResourceInput, EditorActivation, EditorOpenContext, ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { IInstantiationService, IConstructorSignature0, ServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -24,6 +24,7 @@ import { ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/te import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { isEqual } from 'vs/base/common/resources'; import { IPanel } from 'vs/workbench/common/panel'; +import { IRange } from 'vs/editor/common/core/range'; export const DirtyWorkingCopiesContext = new RawContextKey('dirtyWorkingCopies', false); export const ActiveEditorContext = new RawContextKey('activeEditor', null); @@ -983,14 +984,22 @@ export class EditorOptions implements IEditorOptions { /** * Base Text Editor Options. */ -export class TextEditorOptions extends EditorOptions { - private startLineNumber: number | undefined; - private startColumn: number | undefined; - private endLineNumber: number | undefined; - private endColumn: number | undefined; +export class TextEditorOptions extends EditorOptions implements ITextEditorOptions { - private revealInCenterIfOutsideViewport: boolean | undefined; - private editorViewState: IEditorViewState | undefined; + /** + * Text editor selection. + */ + selection: ITextEditorSelection | undefined; + + /** + * Text editor view state. + */ + editorViewState: IEditorViewState | undefined; + + /** + * Option to scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport. + */ + revealInCenterIfOutsideViewport: boolean | undefined; static from(input?: IBaseResourceInput): TextEditorOptions | undefined { if (!input || !input.options) { @@ -1017,8 +1026,12 @@ export class TextEditorOptions extends EditorOptions { super.overwrite(options); if (options.selection) { - const selection = options.selection; - this.selection(selection.startLineNumber, selection.startColumn, selection.endLineNumber, selection.endColumn); + this.selection = { + startLineNumber: options.selection.startLineNumber, + startColumn: options.selection.startColumn, + endLineNumber: options.selection.endLineNumber ?? options.selection.startLineNumber, + endColumn: options.selection.endColumn ?? options.selection.startColumn + }; } if (options.viewState) { @@ -1036,19 +1049,7 @@ export class TextEditorOptions extends EditorOptions { * Returns if this options object has objects defined for the editor. */ hasOptionsDefined(): boolean { - return !!this.editorViewState || (!isUndefinedOrNull(this.startLineNumber) && !isUndefinedOrNull(this.startColumn)); - } - - /** - * Tells the editor to set show the given selection when the editor is being opened. - */ - selection(startLineNumber: number, startColumn: number, endLineNumber: number = startLineNumber, endColumn: number = startColumn): EditorOptions { - this.startLineNumber = startLineNumber; - this.startColumn = startColumn; - this.endLineNumber = endLineNumber; - this.endColumn = endColumn; - - return this; + return !!this.editorViewState || !!this.revealInCenterIfOutsideViewport || !!this.selection; } /** @@ -1069,12 +1070,6 @@ export class TextEditorOptions extends EditorOptions { * @return if something was applied */ apply(editor: ICodeEditor, scrollType: ScrollType): boolean { - - // View state - return this.applyViewState(editor, scrollType); - } - - private applyViewState(editor: ICodeEditor, scrollType: ScrollType): boolean { let gotApplied = false; // First try viewstate @@ -1084,36 +1079,20 @@ export class TextEditorOptions extends EditorOptions { } // Otherwise check for selection - else if (!isUndefinedOrNull(this.startLineNumber) && !isUndefinedOrNull(this.startColumn)) { + else if (this.selection) { + const range: IRange = { + startLineNumber: this.selection.startLineNumber, + startColumn: this.selection.startColumn, + endLineNumber: this.selection.endLineNumber ?? this.selection.startLineNumber, + endColumn: this.selection.endColumn ?? this.selection.startColumn + }; - // Select - if (!isUndefinedOrNull(this.endLineNumber) && !isUndefinedOrNull(this.endColumn)) { - const range = { - startLineNumber: this.startLineNumber, - startColumn: this.startColumn, - endLineNumber: this.endLineNumber, - endColumn: this.endColumn - }; - editor.setSelection(range); - if (this.revealInCenterIfOutsideViewport) { - editor.revealRangeInCenterIfOutsideViewport(range, scrollType); - } else { - editor.revealRangeInCenter(range, scrollType); - } - } + editor.setSelection(range); - // Reveal - else { - const pos = { - lineNumber: this.startLineNumber, - column: this.startColumn - }; - editor.setPosition(pos); - if (this.revealInCenterIfOutsideViewport) { - editor.revealPositionInCenterIfOutsideViewport(pos, scrollType); - } else { - editor.revealPositionInCenter(pos, scrollType); - } + if (this.revealInCenterIfOutsideViewport) { + editor.revealRangeInCenterIfOutsideViewport(range, scrollType); + } else { + editor.revealRangeInCenter(range, scrollType); } gotApplied = true; @@ -1319,13 +1298,13 @@ export async function pathsToEditors(paths: IPathData[] | undefined, fileService const exists = (typeof path.exists === 'boolean') ? path.exists : await fileService.exists(resource); - const options: ITextEditorOptions = { pinned: true }; - if (exists && typeof path.lineNumber === 'number') { - options.selection = { + const options: ITextEditorOptions = (exists && typeof path.lineNumber === 'number') ? { + selection: { startLineNumber: path.lineNumber, startColumn: path.columnNumber || 1 - }; - } + }, + pinned: true + } : { pinned: true }; let input: IResourceInput | IUntitledTextResourceInput; if (!exists) { diff --git a/src/vs/workbench/test/common/editor/editorOptions.test.ts b/src/vs/workbench/test/common/editor/editorOptions.test.ts deleted file mode 100644 index eb6f7540808..00000000000 --- a/src/vs/workbench/test/common/editor/editorOptions.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { EditorOptions, TextEditorOptions } from 'vs/workbench/common/editor'; - -suite('Workbench editor options', () => { - - test('EditorOptions', () => { - let options = new EditorOptions(); - - assert(!options.preserveFocus); - options.preserveFocus = true; - assert(options.preserveFocus); - assert(!options.forceReload); - options.forceReload = true; - assert(options.forceReload); - - options = new EditorOptions(); - options.forceReload = true; - }); - - test('TextEditorOptions', () => { - let options = new TextEditorOptions(); - let otherOptions = new TextEditorOptions(); - - assert(!options.hasOptionsDefined()); - options.selection(1, 1, 2, 2); - assert(options.hasOptionsDefined()); - - otherOptions.selection(1, 1, 2, 2); - - options = new TextEditorOptions(); - options.forceReload = true; - options.selection(1, 1, 2, 2); - }); -}); \ No newline at end of file From 561f5215d73a99203a4e0bd6ea7e856ff869984b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 13 Jan 2020 10:16:40 +0100 Subject: [PATCH 171/843] for rename-file, show the new name more prominent, #77728 --- .../contrib/bulkEdit/browser/bulkEditTree.ts | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 0fbdcd26345..03746a0a7bc 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -160,21 +160,34 @@ class FileElementTemplate { this._localDisposables.add(dom.addDisposableListener(this._checkbox, 'change', (() => element.edit.updateChecked(this._checkbox.checked)))); this._checkbox.checked = element.edit.isChecked(); - this._label.setFile(element.uri, { - matches: createMatches(score), - fileKind: FileKind.FILE, - fileDecorations: { colors: true, badges: false }, - }); - - // details if (element.edit.type & BulkFileOperationType.Rename && element.edit.newUri) { - this._details.innerText = localize('detail.rename', "(renaming to {0})", this._labelService.getUriLabel(element.edit.newUri, { relative: true })); - } else if (element.edit.type & BulkFileOperationType.Create) { - this._details.innerText = localize('detail.create', "(creating)"); - } else if (element.edit.type & BulkFileOperationType.Create) { - this._details.innerText = localize('detail.del', "(deleting)"); + // rename: NEW NAME (old name) + this._label.setFile(element.edit.newUri, { + matches: createMatches(score), + fileKind: FileKind.FILE, + fileDecorations: { colors: true, badges: false }, + }); + + this._details.innerText = localize( + 'detail.rename', "(renaming from {0})", + this._labelService.getUriLabel(element.uri, { relative: true }) + ); + } else { - this._details.innerText = ''; + // create, delete, edit: NAME + this._label.setFile(element.uri, { + matches: createMatches(score), + fileKind: FileKind.FILE, + fileDecorations: { colors: true, badges: false }, + }); + + if (element.edit.type & BulkFileOperationType.Create) { + this._details.innerText = localize('detail.create', "(creating)"); + } else if (element.edit.type & BulkFileOperationType.Create) { + this._details.innerText = localize('detail.del', "(deleting)"); + } else { + this._details.innerText = ''; + } } } } From 5e758c10c969d668730f036abfc557d9cfb62b87 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 Jan 2020 10:17:36 +0100 Subject: [PATCH 172/843] fix compile errors --- .../contrib/search/browser/openSymbolHandler.ts | 7 ++----- src/vs/workbench/services/history/browser/history.ts | 10 +++------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts b/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts index 0f44a755c54..3f01dba4e1d 100644 --- a/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts +++ b/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts @@ -119,14 +119,11 @@ class SymbolEntry extends EditorQuickOpenEntry { const input: IResourceInput = { resource: this.bearing.location.uri, options: { - pinned: !this.configurationService.getValue().workbench.editor.enablePreviewFromQuickOpen + pinned: !this.configurationService.getValue().workbench.editor.enablePreviewFromQuickOpen, + selection: this.bearing.location.range ? Range.collapseToStart(this.bearing.location.range) : undefined } }; - if (this.bearing.location.range) { - input.options!.selection = Range.collapseToStart(this.bearing.location.range); - } - return input; } diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 02ca4db033b..5557d08727d 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -354,15 +354,11 @@ export class HistoryService extends Disposable implements IHistoryService { private doNavigate(location: IStackEntry): Promise { const options: ITextEditorOptions = { - revealIfOpened: true // support to navigate across editor groups + revealIfOpened: true, // support to navigate across editor groups, + selection: location.selection, + revealInCenterIfOutsideViewport: !!location.selection }; - // Support selection and minimize scrolling by setting revealInCenterIfOutsideViewport - if (location.selection) { - options.selection = location.selection; - options.revealInCenterIfOutsideViewport = true; - } - if (location.input instanceof EditorInput) { return this.editorService.openEditor(location.input, options); } From c5771a3cb61fa62ac79bc92418adacd67c78c4aa Mon Sep 17 00:00:00 2001 From: Luis Oliveira Date: Mon, 13 Jan 2020 09:36:48 +0000 Subject: [PATCH 173/843] Small changes to ShadowDOM --- src/vs/base/browser/dom.ts | 2 +- src/vs/editor/browser/editorDom.ts | 130 +++++++++++++++++------------ 2 files changed, 78 insertions(+), 54 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 5b48ad6478d..5bf15cdfa1b 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -826,7 +826,7 @@ export function createStyleSheet(container: HTMLElement): HTMLStyleElement { container = (window as any).monacoShadowRoot.querySelector('head'); } else { - container = document.getElementsByTagName('head')[0] + container = document.getElementsByTagName('head')[0]; } } diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 1fd10f1932d..aeb5c00029c 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -14,10 +14,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; export class PageCoordinates { _pageCoordinatesBrand: void; - constructor( - public readonly x: number, - public readonly y: number - ) { } + constructor(public readonly x: number, public readonly y: number) { } public toClientCoordinates(): ClientCoordinates { return new ClientCoordinates(this.x - dom.StandardWindow.scrollX, this.y - dom.StandardWindow.scrollY); @@ -34,10 +31,7 @@ export class PageCoordinates { export class ClientCoordinates { _clientCoordinatesBrand: void; - constructor( - public readonly clientX: number, - public readonly clientY: number - ) { } + constructor(public readonly clientX: number, public readonly clientY: number) { } public toPageCoordinates(): PageCoordinates { return new PageCoordinates(this.clientX + dom.StandardWindow.scrollX, this.clientY + dom.StandardWindow.scrollY); @@ -88,7 +82,6 @@ export interface EditorMouseEventMerger { } export class EditorMouseEventFactory { - private readonly _editorViewDomNode: HTMLElement; constructor(editorViewDomNode: HTMLElement) { @@ -123,16 +116,29 @@ export class EditorMouseEventFactory { }); } - public onMouseMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { - const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { + public onMouseMoveThrottled( + target: HTMLElement, + callback: (e: EditorMouseEvent) => void, + merger: EditorMouseEventMerger, + minimumTimeMs: number + ): IDisposable { + const myMerger: dom.IEventMerger = ( + lastEvent: EditorMouseEvent | null, + currentEvent: MouseEvent + ): EditorMouseEvent => { return merger(lastEvent, this._create(currentEvent)); }; - return dom.addDisposableThrottledListener(target, 'mousemove', callback, myMerger, minimumTimeMs); + return dom.addDisposableThrottledListener( + target, + 'mousemove', + callback, + myMerger, + minimumTimeMs + ); } } export class EditorPointerEventFactory { - private readonly _editorViewDomNode: HTMLElement; constructor(editorViewDomNode: HTMLElement) { @@ -161,16 +167,29 @@ export class EditorPointerEventFactory { }); } - public onPointerMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { - const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { + public onPointerMoveThrottled( + target: HTMLElement, + callback: (e: EditorMouseEvent) => void, + merger: EditorMouseEventMerger, + minimumTimeMs: number + ): IDisposable { + const myMerger: dom.IEventMerger = ( + lastEvent: EditorMouseEvent | null, + currentEvent: MouseEvent + ): EditorMouseEvent => { return merger(lastEvent, this._create(currentEvent)); }; - return dom.addDisposableThrottledListener(target, 'pointermove', callback, myMerger, minimumTimeMs); + return dom.addDisposableThrottledListener( + target, + 'pointermove', + callback, + myMerger, + minimumTimeMs + ); } } export class GlobalEditorMouseMoveMonitor extends Disposable { - private readonly _editorViewDomNode: HTMLElement; protected readonly _globalMouseMoveMonitor: GlobalMouseMoveMonitor; private _keydownListener: IDisposable | null; @@ -182,20 +201,31 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { this._keydownListener = null; } - public startMonitoring(merger: EditorMouseEventMerger, mouseMoveCallback: (e: EditorMouseEvent) => void, onStopCallback: () => void): void { - + public startMonitoring( + merger: EditorMouseEventMerger, + mouseMoveCallback: (e: EditorMouseEvent) => void, + onStopCallback: () => void + ): void { // Add a <> keydown event listener that will cancel the monitoring // if something other than a modifier key is pressed - this._keydownListener = dom.addStandardDisposableListener(document, 'keydown', (e) => { - const kb = e.toKeybinding(); - if (kb.isModifierKey()) { - // Allow modifier keys - return; - } - this._globalMouseMoveMonitor.stopMonitoring(true); - }, true); + this._keydownListener = dom.addStandardDisposableListener( + document, + 'keydown', + e => { + const kb = e.toKeybinding(); + if (kb.isModifierKey()) { + // Allow modifier keys + return; + } + this._globalMouseMoveMonitor.stopMonitoring(true); + }, + true + ); - const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { + const myMerger: dom.IEventMerger = ( + lastEvent: EditorMouseEvent | null, + currentEvent: MouseEvent + ): EditorMouseEvent => { return merger(lastEvent, new EditorMouseEvent(currentEvent, this._editorViewDomNode)); }; @@ -206,18 +236,12 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { } } - -// Dynamically polyfill the `caretRangeFromPoint` method if (typeof ShadowRoot.prototype.caretRangeFromPoint === 'undefined') { - /** - * The `best I can do` polyfill for this method - */ - ShadowRoot.prototype.caretRangeFromPoint = function (x, y) { - + ShadowRoot.prototype.caretRangeFromPoint = function (x, y) { let range = document.createRange(); - // Get the element under the point - let el:Element | null = this.elementFromPoint(x, y); + // Get the element under the point + let el: Element | null = this.elementFromPoint(x, y); if (el !== null) { // Get the last child of the element until its firstChild is a text node @@ -267,25 +291,25 @@ if (typeof ShadowRoot.prototype.caretRangeFromPoint === 'undefined') { range.setEnd((el as any).firstChild, offset); } - return range; - }; + return range; + }; } (function (window) { - (window as any)._getCharWidth = (char:any, font:any) => { - let cacheKey = char + font; - if ((window as any)._getCharWidth._cache[cacheKey]) { - return (window as any)._getCharWidth._cache[cacheKey]; - } + (window as any)._getCharWidth = (char: any, font: any) => { + let cacheKey = char + font; + if ((window as any)._getCharWidth._cache[cacheKey]) { + return (window as any)._getCharWidth._cache[cacheKey]; + } - let context = (window as any)._getCharWidth._canvas.getContext("2d"); - context.font = font; - let metrics = context.measureText(char); - let width = metrics.width; - (window as any)._getCharWidth._cache[cacheKey] = width; - return width; - } + let context = (window as any)._getCharWidth._canvas.getContext('2d'); + context.font = font; + let metrics = context.measureText(char); + let width = metrics.width; + (window as any)._getCharWidth._cache[cacheKey] = width; + return width; + }; - (window as any)._getCharWidth._cache = {}; - (window as any)._getCharWidth._canvas = document.createElement('canvas'); + (window as any)._getCharWidth._cache = {}; + (window as any)._getCharWidth._canvas = document.createElement('canvas'); })(window); From f36e0c13c68564d5aa1a34bdb6b92ba8fe417ffc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 13 Jan 2020 10:43:20 +0100 Subject: [PATCH 174/843] fix #88310 --- src/vs/workbench/api/common/extHostDocuments.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostDocuments.ts b/src/vs/workbench/api/common/extHostDocuments.ts index d4406d5166a..4e8bb4d1252 100644 --- a/src/vs/workbench/api/common/extHostDocuments.ts +++ b/src/vs/workbench/api/common/extHostDocuments.ts @@ -13,6 +13,7 @@ import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocum import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import type * as vscode from 'vscode'; import { assertIsDefined } from 'vs/base/common/types'; +import { deepFreeze } from 'vs/base/common/objects'; export class ExtHostDocuments implements ExtHostDocumentsShape { @@ -144,7 +145,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { } data._acceptIsDirty(isDirty); data.onEvents(events); - this._onDidChangeDocument.fire({ + this._onDidChangeDocument.fire(deepFreeze({ document: data.document, contentChanges: events.changes.map((change) => { return { @@ -154,7 +155,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { text: change.text }; }) - }); + })); } public setWordDefinitionFor(modeId: string, wordDefinition: RegExp | undefined): void { From 94742854c255977b0806097c68b4a80883caf0c7 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Mon, 13 Jan 2020 10:59:18 +0100 Subject: [PATCH 175/843] Minor tweaks --- .../keybinding/browser/keybindingService.ts | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index e5387e3260d..8bf8e567fe2 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -44,7 +44,7 @@ import { IKeymapService } from 'vs/workbench/services/keybinding/common/keymapIn import { getDispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; import { isArray } from 'vs/base/common/types'; import { INavigatorWithKeyboard, IKeyboard } from 'vs/workbench/services/keybinding/browser/navigatorKeyboard'; -import { ScanCode, ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE, IMMUTABLE_KEY_CODE_TO_CODE } from 'vs/base/common/scanCode'; +import { ScanCode, ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/base/common/scanCode'; import { flatten } from 'vs/base/common/arrays'; import { BrowserFeatures, KeyboardSupport } from 'vs/base/browser/canIUse'; @@ -578,33 +578,38 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { } const code = ScanCodeUtils.toEnum(event.code); - if (NUMPAD_PRINTABLE_SCANCODES.indexOf(code) === -1) { - const keycode = IMMUTABLE_CODE_TO_KEY_CODE[code]; - if (keycode !== -1) { - // https://github.com/microsoft/vscode/issues/74934 - return false; - } - // consult the KeyboardMapperFactory to check the given event for - // a printable value. - const mapping = this.keymapService.getRawKeyboardMapping(); - if (!mapping) { - return false; - } - const keyInfo = mapping[event.code]; - if (!keyInfo) { - return false; - } - if (!keyInfo.value || /\s/.test(keyInfo.value)) { - return false; - } - } else { - const immutableScanCode = IMMUTABLE_KEY_CODE_TO_CODE[event.keyCode]; - if (code !== immutableScanCode) { - // NumLock is disabled - return false; + if (NUMPAD_PRINTABLE_SCANCODES.indexOf(code) !== -1) { + // This is a numpad key that might produce a printable character based on NumLock. + // Let's check if NumLock is on or off based on the event's keyCode. + // e.g. + // - when NumLock is off, ScanCode.Numpad4 produces KeyCode.LeftArrow + // - when NumLock is on, ScanCode.Numpad4 produces KeyCode.NUMPAD_4 + // However, ScanCode.NumpadAdd always produces KeyCode.NUMPAD_ADD + if (event.keyCode === IMMUTABLE_CODE_TO_KEY_CODE[code]) { + // NumLock is on or this is /, *, -, + on the numpad + return true; } + return false; } + const keycode = IMMUTABLE_CODE_TO_KEY_CODE[code]; + if (keycode !== -1) { + // https://github.com/microsoft/vscode/issues/74934 + return false; + } + // consult the KeyboardMapperFactory to check the given event for + // a printable value. + const mapping = this.keymapService.getRawKeyboardMapping(); + if (!mapping) { + return false; + } + const keyInfo = mapping[event.code]; + if (!keyInfo) { + return false; + } + if (!keyInfo.value || /\s/.test(keyInfo.value)) { + return false; + } return true; } } From a6990443cb1f1813948bba71f4f4aeb0a63df6a0 Mon Sep 17 00:00:00 2001 From: Luis Oliveira Date: Mon, 13 Jan 2020 10:04:11 +0000 Subject: [PATCH 176/843] Fix formatting --- src/vs/editor/browser/editorDom.ts | 89 +++++++++++------------------- 1 file changed, 31 insertions(+), 58 deletions(-) diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index aeb5c00029c..91a89670f08 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -14,7 +14,10 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; export class PageCoordinates { _pageCoordinatesBrand: void; - constructor(public readonly x: number, public readonly y: number) { } + constructor( + public readonly x: number, + public readonly y: number + ) { } public toClientCoordinates(): ClientCoordinates { return new ClientCoordinates(this.x - dom.StandardWindow.scrollX, this.y - dom.StandardWindow.scrollY); @@ -31,7 +34,10 @@ export class PageCoordinates { export class ClientCoordinates { _clientCoordinatesBrand: void; - constructor(public readonly clientX: number, public readonly clientY: number) { } + constructor( + public readonly clientX: number, + public readonly clientY: number + ) { } public toPageCoordinates(): PageCoordinates { return new PageCoordinates(this.clientX + dom.StandardWindow.scrollX, this.clientY + dom.StandardWindow.scrollY); @@ -82,6 +88,7 @@ export interface EditorMouseEventMerger { } export class EditorMouseEventFactory { + private readonly _editorViewDomNode: HTMLElement; constructor(editorViewDomNode: HTMLElement) { @@ -116,29 +123,16 @@ export class EditorMouseEventFactory { }); } - public onMouseMoveThrottled( - target: HTMLElement, - callback: (e: EditorMouseEvent) => void, - merger: EditorMouseEventMerger, - minimumTimeMs: number - ): IDisposable { - const myMerger: dom.IEventMerger = ( - lastEvent: EditorMouseEvent | null, - currentEvent: MouseEvent - ): EditorMouseEvent => { + public onMouseMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { + const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { return merger(lastEvent, this._create(currentEvent)); }; - return dom.addDisposableThrottledListener( - target, - 'mousemove', - callback, - myMerger, - minimumTimeMs - ); + return dom.addDisposableThrottledListener(target, 'mousemove', callback, myMerger, minimumTimeMs); } } export class EditorPointerEventFactory { + private readonly _editorViewDomNode: HTMLElement; constructor(editorViewDomNode: HTMLElement) { @@ -167,29 +161,16 @@ export class EditorPointerEventFactory { }); } - public onPointerMoveThrottled( - target: HTMLElement, - callback: (e: EditorMouseEvent) => void, - merger: EditorMouseEventMerger, - minimumTimeMs: number - ): IDisposable { - const myMerger: dom.IEventMerger = ( - lastEvent: EditorMouseEvent | null, - currentEvent: MouseEvent - ): EditorMouseEvent => { + public onPointerMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { + const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { return merger(lastEvent, this._create(currentEvent)); }; - return dom.addDisposableThrottledListener( - target, - 'pointermove', - callback, - myMerger, - minimumTimeMs - ); + return dom.addDisposableThrottledListener(target, 'pointermove', callback, myMerger, minimumTimeMs); } } export class GlobalEditorMouseMoveMonitor extends Disposable { + private readonly _editorViewDomNode: HTMLElement; protected readonly _globalMouseMoveMonitor: GlobalMouseMoveMonitor; private _keydownListener: IDisposable | null; @@ -201,31 +182,20 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { this._keydownListener = null; } - public startMonitoring( - merger: EditorMouseEventMerger, - mouseMoveCallback: (e: EditorMouseEvent) => void, - onStopCallback: () => void - ): void { + public startMonitoring(merger: EditorMouseEventMerger, mouseMoveCallback: (e: EditorMouseEvent) => void, onStopCallback: () => void): void { + // Add a <> keydown event listener that will cancel the monitoring // if something other than a modifier key is pressed - this._keydownListener = dom.addStandardDisposableListener( - document, - 'keydown', - e => { - const kb = e.toKeybinding(); - if (kb.isModifierKey()) { - // Allow modifier keys - return; - } - this._globalMouseMoveMonitor.stopMonitoring(true); - }, - true - ); + this._keydownListener = dom.addStandardDisposableListener(document, 'keydown', (e) => { + const kb = e.toKeybinding(); + if (kb.isModifierKey()) { + // Allow modifier keys + return; + } + this._globalMouseMoveMonitor.stopMonitoring(true); + }, true); - const myMerger: dom.IEventMerger = ( - lastEvent: EditorMouseEvent | null, - currentEvent: MouseEvent - ): EditorMouseEvent => { + const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { return merger(lastEvent, new EditorMouseEvent(currentEvent, this._editorViewDomNode)); }; @@ -236,8 +206,11 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { } } + if (typeof ShadowRoot.prototype.caretRangeFromPoint === 'undefined') { + ShadowRoot.prototype.caretRangeFromPoint = function (x, y) { + let range = document.createRange(); // Get the element under the point From dd54a1439d65cf08090487ef03d6d8b93dd9ae1a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 Jan 2020 11:22:58 +0100 Subject: [PATCH 177/843] debt - move text file model telemetry into telemetry contribution --- .../browser/telemetry.contribution.ts | 124 +++++++++++++++++- .../textfile/common/textFileEditorModel.ts | 112 +--------------- .../common/textFileEditorModelManager.ts | 6 + .../services/textfile/common/textfiles.ts | 2 + 4 files changed, 131 insertions(+), 113 deletions(-) diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 35c131834e0..eca81528ce4 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -19,20 +19,46 @@ import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; import { configurationTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { ITextFileService, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles'; +import { extname, basename, isEqual, isEqualOrParent, joinPath } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import { guessMimeTypes } from 'vs/base/common/mime'; +import { hash } from 'vs/base/common/hash'; + +type TelemetryData = { + mimeType: string; + ext: string; + path: number; + reason?: number; + whitelistedjson?: string; +}; + +type FileTelemetryDataFragment = { + mimeType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + ext: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + path: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + reason?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + whitelistedjson?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; +}; export class TelemetryContribution extends Disposable implements IWorkbenchContribution { + private static WHITELIST_JSON = ['package.json', 'package-lock.json', 'tsconfig.json', 'jsconfig.json', 'bower.json', '.eslintrc.json', 'tslint.json', 'composer.json']; + private static WHITELIST_WORKSPACE_JSON = ['settings.json', 'extensions.json', 'tasks.json', 'launch.json']; + constructor( - @ITelemetryService telemetryService: ITelemetryService, - @IWorkspaceContextService contextService: IWorkspaceContextService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IActivityBarService activityBarService: IActivityBarService, @ILifecycleService lifecycleService: ILifecycleService, @IEditorService editorService: IEditorService, @IKeybindingService keybindingsService: IKeybindingService, @IWorkbenchThemeService themeService: IWorkbenchThemeService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IConfigurationService configurationService: IConfigurationService, - @IViewletService viewletService: IViewletService + @IViewletService viewletService: IViewletService, + @ITextFileService textFileService: ITextFileService, ) { super(); @@ -45,6 +71,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr outerHeight: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; outerWidth: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; }; + type WorkspaceLoadClassification = { userAgent: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; emptyWorkbench: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; @@ -59,6 +86,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr restoredEditors: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; startupKind: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; }; + type WorkspaceLoadEvent = { userAgent: string; windowSize: { innerHeight: number, innerWidth: number, outerHeight: number, outerWidth: number }; @@ -73,6 +101,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr restoredEditors: number; startupKind: StartupKind; }; + telemetryService.publicLog2('workspaceLoad', { userAgent: navigator.userAgent, windowSize: { innerHeight: window.innerHeight, innerWidth: window.innerWidth, outerHeight: window.outerHeight, outerWidth: window.outerWidth }, @@ -94,9 +123,94 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr // Configuration Telemetry this._register(configurationTelemetry(telemetryService, configurationService)); + // Files Telemetry + this._register(textFileService.models.onModelLoaded(e => this.onTextFileModelLoaded(e))); + this._register(textFileService.models.onModelSaved(e => this.onTextFileModelSaved(e))); + // Lifecycle this._register(lifecycleService.onShutdown(() => this.dispose())); } + + private onTextFileModelLoaded(event: TextFileModelChangeEvent): void { + const settingsType = this.getTypeIfSettings(event.resource); + if (settingsType) { + type SettingsReadClassification = { + settingsType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + }; + + this.telemetryService.publicLog2<{ settingsType: string }, SettingsReadClassification>('settingsRead', { settingsType }); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data + } else { + type FileGetClassification = {} & FileTelemetryDataFragment; + + this.telemetryService.publicLog2('fileGet', this.getTelemetryData(event.resource)); + } + } + + private onTextFileModelSaved(event: TextFileModelChangeEvent): void { + const settingsType = this.getTypeIfSettings(event.resource); + if (settingsType) { + type SettingsWrittenClassification = { + settingsType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + }; + this.telemetryService.publicLog2<{ settingsType: string }, SettingsWrittenClassification>('settingsWritten', { settingsType }); // Do not log write to user settings.json and .vscode folder as a filePUT event as it ruins our JSON usage data + } else { + type FilePutClassfication = {} & FileTelemetryDataFragment; + this.telemetryService.publicLog2('filePUT', this.getTelemetryData(event.resource)); + } + } + + private getTypeIfSettings(resource: URI): string { + if (extname(resource) !== '.json') { + return ''; + } + + // Check for global settings file + if (isEqual(resource, this.environmentService.settingsResource)) { + return 'global-settings'; + } + + // Check for keybindings file + if (isEqual(resource, this.environmentService.keybindingsResource)) { + return 'keybindings'; + } + + // Check for snippets + if (isEqualOrParent(resource, joinPath(this.environmentService.userRoamingDataHome, 'snippets'))) { + return 'snippets'; + } + + // Check for workspace settings file + const folders = this.contextService.getWorkspace().folders; + for (const folder of folders) { + if (isEqualOrParent(resource, folder.toResource('.vscode'))) { + const filename = basename(resource); + if (TelemetryContribution.WHITELIST_WORKSPACE_JSON.indexOf(filename) > -1) { + return `.vscode/${filename}`; + } + } + } + + return ''; + } + + private getTelemetryData(resource: URI, reason?: number): TelemetryData { + const ext = extname(resource); + const fileName = basename(resource); + const path = resource.scheme === Schemas.file ? resource.fsPath : resource.path; + const telemetryData = { + mimeType: guessMimeTypes(resource).join(', '), + ext, + path: hash(path), + reason, + whitelistedjson: undefined as string | undefined + }; + + if (ext === '.json' && TelemetryContribution.WHITELIST_JSON.indexOf(fileName) > -1) { + telemetryData['whitelistedjson'] = fileName; + } + + return telemetryData; + } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TelemetryContribution, LifecyclePhase.Restored); \ No newline at end of file +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TelemetryContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 19ade6f51be..22c6313771f 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -5,13 +5,10 @@ import * as nls from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; -import { guessMimeTypes } from 'vs/base/common/mime'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { URI } from 'vs/base/common/uri'; import { assertIsDefined } from 'vs/base/common/types'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ITextFileService, ModelState, ITextFileEditorModel, ISaveErrorHandler, ISaveParticipant, StateChange, ITextFileStreamContent, ILoadOptions, LoadReason, IResolvedTextFileEditorModel, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ModelState, ITextFileEditorModel, ISaveErrorHandler, ISaveParticipant, StateChange, ITextFileStreamContent, ILoadOptions, IResolvedTextFileEditorModel, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { EncodingMode, IRevertOptions, SaveReason } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -19,15 +16,12 @@ import { IFileService, FileOperationError, FileOperationResult, FileChangesEvent import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { timeout } from 'vs/base/common/async'; import { ITextBufferFactory } from 'vs/editor/common/model'; -import { hash } from 'vs/base/common/hash'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ILogService } from 'vs/platform/log/common/log'; -import { isEqual, isEqualOrParent, extname, basename, joinPath } from 'vs/base/common/resources'; +import { isEqual, basename } from 'vs/base/common/resources'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { Schemas } from 'vs/base/common/network'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; @@ -39,30 +33,11 @@ export interface IBackupMetaData { orphaned: boolean; } -type FileTelemetryDataFragment = { - mimeType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - ext: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - path: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - reason?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; - whitelistedjson?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; -}; - -type TelemetryData = { - mimeType: string; - ext: string; - path: number; - reason?: number; - whitelistedjson?: string; -}; - /** * The text file editor model listens to changes to its underlying code editor model and saves these changes through the file service back to the disk. */ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFileEditorModel { - static WHITELIST_JSON = ['package.json', 'package-lock.json', 'tsconfig.json', 'jsconfig.json', 'bower.json', '.eslintrc.json', 'tslint.json', 'composer.json']; - static WHITELIST_WORKSPACE_JSON = ['settings.json', 'extensions.json', 'tasks.json', 'launch.json']; - private static saveErrorHandler: ISaveErrorHandler; static setSaveErrorHandler(handler: ISaveErrorHandler): void { TextFileEditorModel.saveErrorHandler = handler; } @@ -106,11 +81,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil @IModelService modelService: IModelService, @IFileService private readonly fileService: IFileService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @ITelemetryService private readonly telemetryService: ITelemetryService, @ITextFileService private readonly textFileService: ITextFileService, @IBackupFileService private readonly backupFileService: IBackupFileService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @ILogService private readonly logService: ILogService, @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService @@ -414,19 +386,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.doCreateTextModel(content.resource, content.value, !!fromBackup); } - // Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype - const settingsType = this.getTypeIfSettings(); - if (settingsType) { - type SettingsReadClassification = { - settingsType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - }; - - this.telemetryService.publicLog2<{ settingsType: string }, SettingsReadClassification>('settingsRead', { settingsType }); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data - } else { - type FileGetClassification = {} & FileTelemetryDataFragment; - - this.telemetryService.publicLog2('fileGet', this.getTelemetryData(options?.reason ?? LoadReason.OTHER)); - } + // Emit as event + this._onDidChangeState.fire(StateChange.LOADED); return this; } @@ -699,18 +660,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Emit Events this._onDidChangeState.fire(StateChange.SAVED); this._onDidChangeDirty.fire(); - - // Telemetry - const settingsType = this.getTypeIfSettings(); - if (settingsType) { - type SettingsWrittenClassification = { - settingsType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - }; - this.telemetryService.publicLog2<{ settingsType: string }, SettingsWrittenClassification>('settingsWritten', { settingsType }); // Do not log write to user settings.json and .vscode folder as a filePUT event as it ruins our JSON usage data - } else { - type FilePutClassfication = {} & FileTelemetryDataFragment; - this.telemetryService.publicLog2('filePUT', this.getTelemetryData(options.reason)); - } }, error => { this.logService.error(`[text file model] doSave(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource.toString()); @@ -731,59 +680,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil })); } - private getTypeIfSettings(): string { - if (extname(this.resource) !== '.json') { - return ''; - } - - // Check for global settings file - if (isEqual(this.resource, this.environmentService.settingsResource)) { - return 'global-settings'; - } - - // Check for keybindings file - if (isEqual(this.resource, this.environmentService.keybindingsResource)) { - return 'keybindings'; - } - - // Check for snippets - if (isEqualOrParent(this.resource, joinPath(this.environmentService.userRoamingDataHome, 'snippets'))) { - return 'snippets'; - } - - // Check for workspace settings file - const folders = this.contextService.getWorkspace().folders; - for (const folder of folders) { - if (isEqualOrParent(this.resource, folder.toResource('.vscode'))) { - const filename = basename(this.resource); - if (TextFileEditorModel.WHITELIST_WORKSPACE_JSON.indexOf(filename) > -1) { - return `.vscode/${filename}`; - } - } - } - - return ''; - } - - private getTelemetryData(reason: number | undefined): TelemetryData { - const ext = extname(this.resource); - const fileName = basename(this.resource); - const path = this.resource.scheme === Schemas.file ? this.resource.fsPath : this.resource.path; - const telemetryData = { - mimeType: guessMimeTypes(this.resource).join(', '), - ext, - path: hash(path), - reason, - whitelistedjson: undefined as string | undefined - }; - - if (ext === '.json' && TextFileEditorModel.WHITELIST_JSON.indexOf(fileName) > -1) { - telemetryData['whitelistedjson'] = fileName; - } - - return telemetryData; - } - private doTouch(versionId: number): Promise { if (!this.isResolved()) { return Promise.resolve(); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index deb34c132fb..523f371578a 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -18,6 +18,9 @@ import { onUnexpectedError } from 'vs/base/common/errors'; export class TextFileEditorModelManager extends Disposable implements ITextFileEditorModelManager { + private readonly _onModelLoaded = this._register(new Emitter()); + readonly onModelLoaded = this._onModelLoaded.event; + private readonly _onModelDirty = this._register(new Emitter()); readonly onModelDirty = this._onModelDirty.event; @@ -171,6 +174,9 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE this.mapResourceToStateChangeListener.set(resource, model.onDidChangeState(state => { const event = new TextFileModelChangeEvent(newModel, state); switch (state) { + case StateChange.LOADED: + this._onModelLoaded.fire(event); + break; case StateChange.DIRTY: this._onModelDirty.fire(event); break; diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 8a0439d45de..c5efc94245d 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -276,6 +276,7 @@ export const enum ModelState { } export const enum StateChange { + LOADED, DIRTY, SAVE_ERROR, SAVED, @@ -380,6 +381,7 @@ export interface ITextFileEditorModelManager { readonly onModelEncodingChanged: Event; readonly onModelOrphanedChanged: Event; + readonly onModelLoaded: Event; readonly onModelDirty: Event; readonly onModelSaveError: Event; readonly onModelSaved: Event; From bc0f81bd0545af1eb0282870a95b0f156811b48e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 Jan 2020 11:42:13 +0100 Subject: [PATCH 178/843] debt - cleanup file model a bit --- .../textfile/common/saveSequenzializer.ts | 95 +++++++++++++++ .../textfile/common/textFileEditorModel.ts | 110 ++---------------- .../textfile/test/saveSequenzializer.test.ts | 90 ++++++++++++++ .../textfile/test/textFileEditorModel.test.ts | 81 +------------ .../test/textFileEditorModelManager.test.ts | 11 ++ .../common/textResourcePropertiesService.ts | 0 src/vs/workbench/workbench.common.main.ts | 2 +- 7 files changed, 206 insertions(+), 183 deletions(-) create mode 100644 src/vs/workbench/services/textfile/common/saveSequenzializer.ts create mode 100644 src/vs/workbench/services/textfile/test/saveSequenzializer.test.ts rename src/vs/workbench/services/{textfile => textresourceProperties}/common/textResourcePropertiesService.ts (100%) diff --git a/src/vs/workbench/services/textfile/common/saveSequenzializer.ts b/src/vs/workbench/services/textfile/common/saveSequenzializer.ts new file mode 100644 index 00000000000..2b7cc0c8282 --- /dev/null +++ b/src/vs/workbench/services/textfile/common/saveSequenzializer.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +interface IPendingSave { + versionId: number; + promise: Promise; +} + +interface ISaveOperation { + promise: Promise; + promiseResolve: () => void; + promiseReject: (error: Error) => void; + run: () => Promise; +} + +export class SaveSequentializer { + private _pendingSave?: IPendingSave; + private _nextSave?: ISaveOperation; + + hasPendingSave(versionId?: number): boolean { + if (!this._pendingSave) { + return false; + } + + if (typeof versionId === 'number') { + return this._pendingSave.versionId === versionId; + } + + return !!this._pendingSave; + } + + get pendingSave(): Promise | undefined { + return this._pendingSave ? this._pendingSave.promise : undefined; + } + + setPending(versionId: number, promise: Promise): Promise { + this._pendingSave = { versionId, promise }; + + promise.then(() => this.donePending(versionId), () => this.donePending(versionId)); + + return promise; + } + + private donePending(versionId: number): void { + if (this._pendingSave && versionId === this._pendingSave.versionId) { + + // only set pending to done if the promise finished that is associated with that versionId + this._pendingSave = undefined; + + // schedule the next save now that we are free if we have any + this.triggerNextSave(); + } + } + + private triggerNextSave(): void { + if (this._nextSave) { + const saveOperation = this._nextSave; + this._nextSave = undefined; + + // Run next save and complete on the associated promise + saveOperation.run().then(saveOperation.promiseResolve, saveOperation.promiseReject); + } + } + + setNext(run: () => Promise): Promise { + + // this is our first next save, so we create associated promise with it + // so that we can return a promise that completes when the save operation + // has completed. + if (!this._nextSave) { + let promiseResolve: () => void; + let promiseReject: (error: Error) => void; + const promise = new Promise((resolve, reject) => { + promiseResolve = resolve; + promiseReject = reject; + }); + + this._nextSave = { + run, + promise, + promiseResolve: promiseResolve!, + promiseReject: promiseReject! + }; + } + + // we have a previous next save, just overwrite it + else { + this._nextSave.run = run; + } + + return this._nextSave.promise; + } +} diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 22c6313771f..8293240037e 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -13,7 +13,6 @@ import { EncodingMode, IRevertOptions, SaveReason } from 'vs/workbench/common/ed import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IFileService, FileOperationError, FileOperationResult, FileChangesEvent, FileChangeType, IFileStatWithMetadata, ETAG_DISABLED, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { timeout } from 'vs/base/common/async'; @@ -24,8 +23,9 @@ import { isEqual, basename } from 'vs/base/common/resources'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { SaveSequentializer } from 'vs/workbench/services/textfile/common/saveSequenzializer'; -export interface IBackupMetaData { +interface IBackupMetaData { mtime: number; ctime: number; size: number; @@ -80,7 +80,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil @IModeService modeService: IModeService, @IModelService modelService: IModelService, @IFileService private readonly fileService: IFileService, - @IInstantiationService private readonly instantiationService: IInstantiationService, @ITextFileService private readonly textFileService: ITextFileService, @IBackupFileService private readonly backupFileService: IBackupFileService, @ILogService private readonly logService: ILogService, @@ -755,7 +754,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Prepare handler if (!TextFileEditorModel.saveErrorHandler) { - TextFileEditorModel.setSaveErrorHandler(this.instantiationService.createInstance(DefaultSaveErrorHandler)); + const notificationService = this.notificationService; + TextFileEditorModel.setSaveErrorHandler({ + onSaveError(error: Error, model: TextFileEditorModel): void { + notificationService.error(nls.localize('genericSaveError', "Failed to save '{0}': {1}", basename(model.resource), toErrorMessage(error, false))); + } + }); } // Handle @@ -887,102 +891,4 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } -interface IPendingSave { - versionId: number; - promise: Promise; -} -interface ISaveOperation { - promise: Promise; - promiseResolve: () => void; - promiseReject: (error: Error) => void; - run: () => Promise; -} - -export class SaveSequentializer { - private _pendingSave?: IPendingSave; - private _nextSave?: ISaveOperation; - - hasPendingSave(versionId?: number): boolean { - if (!this._pendingSave) { - return false; - } - - if (typeof versionId === 'number') { - return this._pendingSave.versionId === versionId; - } - - return !!this._pendingSave; - } - - get pendingSave(): Promise | undefined { - return this._pendingSave ? this._pendingSave.promise : undefined; - } - - setPending(versionId: number, promise: Promise): Promise { - this._pendingSave = { versionId, promise }; - - promise.then(() => this.donePending(versionId), () => this.donePending(versionId)); - - return promise; - } - - private donePending(versionId: number): void { - if (this._pendingSave && versionId === this._pendingSave.versionId) { - - // only set pending to done if the promise finished that is associated with that versionId - this._pendingSave = undefined; - - // schedule the next save now that we are free if we have any - this.triggerNextSave(); - } - } - - private triggerNextSave(): void { - if (this._nextSave) { - const saveOperation = this._nextSave; - this._nextSave = undefined; - - // Run next save and complete on the associated promise - saveOperation.run().then(saveOperation.promiseResolve, saveOperation.promiseReject); - } - } - - setNext(run: () => Promise): Promise { - - // this is our first next save, so we create associated promise with it - // so that we can return a promise that completes when the save operation - // has completed. - if (!this._nextSave) { - let promiseResolve: () => void; - let promiseReject: (error: Error) => void; - const promise = new Promise((resolve, reject) => { - promiseResolve = resolve; - promiseReject = reject; - }); - - this._nextSave = { - run, - promise, - promiseResolve: promiseResolve!, - promiseReject: promiseReject! - }; - } - - // we have a previous next save, just overwrite it - else { - this._nextSave.run = run; - } - - return this._nextSave.promise; - } -} - -class DefaultSaveErrorHandler implements ISaveErrorHandler { - - constructor(@INotificationService private readonly notificationService: INotificationService) { } - - onSaveError(error: Error, model: TextFileEditorModel): void { - this.notificationService.error(nls.localize('genericSaveError', "Failed to save '{0}': {1}", basename(model.resource), toErrorMessage(error, false))); - } -} diff --git a/src/vs/workbench/services/textfile/test/saveSequenzializer.test.ts b/src/vs/workbench/services/textfile/test/saveSequenzializer.test.ts new file mode 100644 index 00000000000..c85a38bd8b3 --- /dev/null +++ b/src/vs/workbench/services/textfile/test/saveSequenzializer.test.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { timeout } from 'vs/base/common/async'; +import { SaveSequentializer } from 'vs/workbench/services/textfile/common/saveSequenzializer'; + +suite('Files - SaveSequentializer', () => { + + test('SaveSequentializer - pending basics', async function () { + const sequentializer = new SaveSequentializer(); + + assert.ok(!sequentializer.hasPendingSave()); + assert.ok(!sequentializer.hasPendingSave(2323)); + assert.ok(!sequentializer.pendingSave); + + // pending removes itself after done + await sequentializer.setPending(1, Promise.resolve()); + assert.ok(!sequentializer.hasPendingSave()); + assert.ok(!sequentializer.hasPendingSave(1)); + assert.ok(!sequentializer.pendingSave); + + // pending removes itself after done (use timeout) + sequentializer.setPending(2, timeout(1)); + assert.ok(sequentializer.hasPendingSave()); + assert.ok(sequentializer.hasPendingSave(2)); + assert.ok(!sequentializer.hasPendingSave(1)); + assert.ok(sequentializer.pendingSave); + + await timeout(2); + assert.ok(!sequentializer.hasPendingSave()); + assert.ok(!sequentializer.hasPendingSave(2)); + assert.ok(!sequentializer.pendingSave); + }); + + test('SaveSequentializer - pending and next (finishes instantly)', async function () { + const sequentializer = new SaveSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, timeout(1).then(() => { pendingDone = true; return; })); + + // next finishes instantly + let nextDone = false; + const res = sequentializer.setNext(() => Promise.resolve(null).then(() => { nextDone = true; return; })); + + await res; + assert.ok(pendingDone); + assert.ok(nextDone); + }); + + test('SaveSequentializer - pending and next (finishes after timeout)', async function () { + const sequentializer = new SaveSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, timeout(1).then(() => { pendingDone = true; return; })); + + // next finishes after timeout + let nextDone = false; + const res = sequentializer.setNext(() => timeout(1).then(() => { nextDone = true; return; })); + + await res; + assert.ok(pendingDone); + assert.ok(nextDone); + }); + + test('SaveSequentializer - pending and multiple next (last one wins)', async function () { + const sequentializer = new SaveSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, timeout(1).then(() => { pendingDone = true; return; })); + + // next finishes after timeout + let firstDone = false; + let firstRes = sequentializer.setNext(() => timeout(2).then(() => { firstDone = true; return; })); + + let secondDone = false; + let secondRes = sequentializer.setNext(() => timeout(3).then(() => { secondDone = true; return; })); + + let thirdDone = false; + let thirdRes = sequentializer.setNext(() => timeout(4).then(() => { thirdDone = true; return; })); + + await Promise.all([firstRes, secondRes, thirdRes]); + assert.ok(pendingDone); + assert.ok(!firstDone); + assert.ok(!secondDone); + assert.ok(thirdDone); + }); +}); diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index 5d0f0212cad..a3b647521f7 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { EncodingMode } from 'vs/workbench/common/editor'; -import { TextFileEditorModel, SaveSequentializer } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { ITextFileService, ModelState, StateChange, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { workbenchInstantiationService, TestTextFileService, createFileInput, TestFileService } from 'vs/workbench/test/workbenchTestServices'; import { toResource } from 'vs/base/test/common/utils'; @@ -475,83 +475,4 @@ suite('Files - TextFileEditorModel', () => { await model.save(); model.dispose(); }); - - test('SaveSequentializer - pending basics', async function () { - const sequentializer = new SaveSequentializer(); - - assert.ok(!sequentializer.hasPendingSave()); - assert.ok(!sequentializer.hasPendingSave(2323)); - assert.ok(!sequentializer.pendingSave); - - // pending removes itself after done - await sequentializer.setPending(1, Promise.resolve()); - assert.ok(!sequentializer.hasPendingSave()); - assert.ok(!sequentializer.hasPendingSave(1)); - assert.ok(!sequentializer.pendingSave); - - // pending removes itself after done (use timeout) - sequentializer.setPending(2, timeout(1)); - assert.ok(sequentializer.hasPendingSave()); - assert.ok(sequentializer.hasPendingSave(2)); - assert.ok(!sequentializer.hasPendingSave(1)); - assert.ok(sequentializer.pendingSave); - - await timeout(2); - assert.ok(!sequentializer.hasPendingSave()); - assert.ok(!sequentializer.hasPendingSave(2)); - assert.ok(!sequentializer.pendingSave); - }); - - test('SaveSequentializer - pending and next (finishes instantly)', async function () { - const sequentializer = new SaveSequentializer(); - - let pendingDone = false; - sequentializer.setPending(1, timeout(1).then(() => { pendingDone = true; return; })); - - // next finishes instantly - let nextDone = false; - const res = sequentializer.setNext(() => Promise.resolve(null).then(() => { nextDone = true; return; })); - - await res; - assert.ok(pendingDone); - assert.ok(nextDone); - }); - - test('SaveSequentializer - pending and next (finishes after timeout)', async function () { - const sequentializer = new SaveSequentializer(); - - let pendingDone = false; - sequentializer.setPending(1, timeout(1).then(() => { pendingDone = true; return; })); - - // next finishes after timeout - let nextDone = false; - const res = sequentializer.setNext(() => timeout(1).then(() => { nextDone = true; return; })); - - await res; - assert.ok(pendingDone); - assert.ok(nextDone); - }); - - test('SaveSequentializer - pending and multiple next (last one wins)', async function () { - const sequentializer = new SaveSequentializer(); - - let pendingDone = false; - sequentializer.setPending(1, timeout(1).then(() => { pendingDone = true; return; })); - - // next finishes after timeout - let firstDone = false; - let firstRes = sequentializer.setNext(() => timeout(2).then(() => { firstDone = true; return; })); - - let secondDone = false; - let secondRes = sequentializer.setNext(() => timeout(3).then(() => { secondDone = true; return; })); - - let thirdDone = false; - let thirdRes = sequentializer.setNext(() => timeout(4).then(() => { thirdDone = true; return; })); - - await Promise.all([firstRes, secondRes, thirdRes]); - assert.ok(pendingDone); - assert.ok(!firstDone); - assert.ok(!secondDone); - assert.ok(thirdDone); - }); }); diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts index 4145c7f6f8f..8c9e2e98670 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -141,11 +141,18 @@ suite('Files - TextFileEditorModelManager', () => { const resource1 = toResource.call(this, '/path/index.txt'); const resource2 = toResource.call(this, '/path/other.txt'); + let loadedCounter = 0; let dirtyCounter = 0; let revertedCounter = 0; let savedCounter = 0; let encodingCounter = 0; + manager.onModelLoaded(e => { + if (e.resource.toString() === resource1.toString()) { + loadedCounter++; + } + }); + manager.onModelDirty(e => { if (e.resource.toString() === resource1.toString()) { dirtyCounter++; @@ -171,10 +178,14 @@ suite('Files - TextFileEditorModelManager', () => { }); const model1 = await manager.loadOrCreate(resource1, { encoding: 'utf8' }); + assert.equal(loadedCounter, 1); + accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.DELETED }])); accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.ADDED }])); const model2 = await manager.loadOrCreate(resource2, { encoding: 'utf8' }); + assert.equal(loadedCounter, 2); + model1.textEditorModel!.setValue('changed'); model1.updatePreferredEncoding('utf16'); diff --git a/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts b/src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService.ts similarity index 100% rename from src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts rename to src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService.ts diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index c437848af7e..ab49435a19b 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -71,7 +71,7 @@ import 'vs/workbench/services/history/browser/history'; import 'vs/workbench/services/activity/browser/activityService'; import 'vs/workbench/services/keybinding/browser/keybindingService'; import 'vs/workbench/services/untitled/common/untitledTextEditorService'; -import 'vs/workbench/services/textfile/common/textResourcePropertiesService'; +import 'vs/workbench/services/textresourceProperties/common/textResourcePropertiesService'; import 'vs/workbench/services/mode/common/workbenchModeService'; import 'vs/workbench/services/commands/common/commandService'; import 'vs/workbench/services/themes/browser/workbenchThemeService'; From 0d9ebc47d3f1c5059f379b0572b27b828a81122b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 13 Jan 2020 12:06:24 +0100 Subject: [PATCH 179/843] add setting for semantic highlighting --- src/vs/editor/common/config/editorOptions.ts | 55 ++++++++++++++++++- .../common/services/modelServiceImpl.ts | 52 +++++++++++++++--- src/vs/monaco.d.ts | 11 ++++ 3 files changed, 109 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 94af6b10d1b..990d885089a 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1536,6 +1536,55 @@ class EditorHover extends BaseEditorOption>; + +class EditorSemanticHighlighting extends BaseEditorOption { + + constructor() { + const defaults: EditorSemanticHighlightingOptions = { + enabled: true + }; + super( + EditorOption.semanticHighlighting, 'semanticHighlighting', defaults, + { + 'editor.semanticHighlighting.enabled': { + type: 'boolean', + default: defaults.enabled, + description: nls.localize('semanticHighlighting.enabled', "Controls whether the semanticHighlighting is shown for the languages that support it.") + } + } + ); + } + + public validate(_input: any): EditorSemanticHighlightingOptions { + if (typeof _input !== 'object') { + return this.defaultValue; + } + const input = _input as IEditorSemanticHighlightingOptions; + return { + enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled) + }; + } +} + +//#endregion + //#region layoutInfo /** @@ -2823,7 +2872,7 @@ class EditorSuggest extends BaseEditorOption this._updateModelOptions()); this._updateModelOptions(); - this._register(new SemanticColoringFeature(this, themeService, logService)); + this._register(new SemanticColoringFeature(this, themeService, configurationService, logService)); } private static _readModelOptions(config: IRawConfig, isForSimpleWidget: boolean): ITextModelCreationOptions { @@ -442,20 +442,56 @@ export interface ILineSequence { } class SemanticColoringFeature extends Disposable { + + private static readonly SETTING_ID = 'editor.semanticHighlighting'; + private _watchers: Record; private _semanticStyling: SemanticStyling; + private _configurationService: IConfigurationService; - constructor(modelService: IModelService, themeService: IThemeService, logService: ILogService) { + constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, logService: ILogService) { super(); + this._configurationService = configurationService; this._watchers = Object.create(null); this._semanticStyling = this._register(new SemanticStyling(themeService, logService)); - this._register(modelService.onModelAdded((model) => { + + const isSemanticColoringEnabled = (model: ITextModel) => { + return configurationService.getValue(SemanticColoringFeature.SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri }).enabled; + }; + const register = (model: ITextModel) => { this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling); + }; + const deregister = (model: ITextModel, modelSemanticColoring: ModelSemanticColoring) => { + modelSemanticColoring.dispose(); + delete this._watchers[model.uri.toString()]; + }; + this._register(modelService.onModelAdded((model) => { + if (isSemanticColoringEnabled(model)) { + register(model); + } })); this._register(modelService.onModelRemoved((model) => { - this._watchers[model.uri.toString()].dispose(); - delete this._watchers[model.uri.toString()]; + const curr = this._watchers[model.uri.toString()]; + if (curr) { + deregister(model, curr); + } })); + this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(SemanticColoringFeature.SETTING_ID)) { + for (let model of modelService.getModels()) { + const curr = this._watchers[model.uri.toString()]; + if (isSemanticColoringEnabled(model)) { + if (!curr) { + register(model); + } + } else { + if (curr) { + deregister(model, curr); + } + } + } + } + }); } } @@ -685,7 +721,6 @@ class ModelSemanticColoring extends Disposable { } public dispose(): void { - this._isDisposed = true; if (this._currentResponse) { this._currentResponse.dispose(); this._currentResponse = null; @@ -694,6 +729,9 @@ class ModelSemanticColoring extends Disposable { this._currentRequestCancellationTokenSource.cancel(); this._currentRequestCancellationTokenSource = null; } + this._setSemanticTokens(null, null, null, []); + this._isDisposed = true; + super.dispose(); } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index ace85852ace..a58453dca5e 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3088,6 +3088,17 @@ declare namespace monaco.editor { sticky?: boolean; } + /** + * Configuration options for semantic highlighting + */ + export interface IEditorSemanticHighlightingOptions { + /** + * Enable semantic highlighting. + * Defaults to true. + */ + enabled?: boolean; + } + /** * A description for the overview ruler position. */ From 9c4aa6d903f1ad9075dfbd886e7b0efbfc9c406a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 13 Jan 2020 11:54:26 +0100 Subject: [PATCH 180/843] :lipstick: --- src/vs/editor/contrib/suggest/suggest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index 72ca092ea5e..f79dbe4a54c 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -30,6 +30,7 @@ export class CompletionItem { _brand!: 'ISuggestionItem'; readonly resolve: (token: CancellationToken) => Promise; + isResolved: boolean = false; // readonly editStart: IPosition; @@ -44,7 +45,6 @@ export class CompletionItem { // sorting, filtering score: FuzzyScore = FuzzyScore.Default; distance: number = 0; - isResolved: boolean = false; idx?: number; word?: string; From 0dfc47f4ef3352c8faa73aa0fa7a1414e7134351 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 Jan 2020 12:19:08 +0100 Subject: [PATCH 181/843] use set instead of array --- .../configuration/common/configurationRegistry.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index b62c02f4000..61590f38fc4 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -11,6 +11,7 @@ import * as types from 'vs/base/common/types'; import * as strings from 'vs/base/common/strings'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { values } from 'vs/base/common/map'; export const Extensions = { Configuration: 'base.contributions.configuration' @@ -157,7 +158,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { private readonly configurationProperties: { [qualifiedKey: string]: IJSONSchema }; private readonly excludedConfigurationProperties: { [qualifiedKey: string]: IJSONSchema }; private readonly resourceLanguageSettingsSchema: IJSONSchema; - private readonly overrideIdentifiers: string[] = []; + private readonly overrideIdentifiers = new Set(); private overridePropertyPattern: string; private readonly _onDidSchemaChange = new Emitter(); @@ -290,7 +291,10 @@ class ConfigurationRegistry implements IConfigurationRegistry { } public registerOverrideIdentifiers(overrideIdentifiers: string[]): void { - this.overrideIdentifiers.push(...overrideIdentifiers); + for (const overrideIdentifier of overrideIdentifiers) { + this.overrideIdentifiers.add(overrideIdentifier); + } + this.updateOverridePropertyPatternKey(); } @@ -420,7 +424,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { } private computeOverridePropertyPattern(): string { - return this.overrideIdentifiers.length ? OVERRIDE_PATTERN_WITH_SUBSTITUTION.replace('${0}', this.overrideIdentifiers.map(identifier => strings.createRegExp(identifier, false).source).join('|')) : OVERRIDE_PROPERTY; + return this.overrideIdentifiers.size > 0 ? OVERRIDE_PATTERN_WITH_SUBSTITUTION.replace('${0}', values(this.overrideIdentifiers).map(identifier => strings.createRegExp(identifier, false).source).join('|')) : OVERRIDE_PROPERTY; } } From e69cc0760c1090cb826c6e59542c551b3e2432d6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 13 Jan 2020 12:26:42 +0100 Subject: [PATCH 182/843] update telemetry message with index of changed field #87187 --- .../api/common/extHostLanguageFeatures.ts | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index af9a04c90f3..adfbebe622a 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -826,23 +826,27 @@ class SuggestAdapter { type BlameExtension = { extensionId: string; - kind: string + kind: string; + index: string; }; type BlameExtensionMeta = { extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; kind: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + index: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; }; - if (!this._didWarnMust && _mustNotChange !== SuggestAdapter._mustNotChangeHash(resolvedItem)) { + let _mustNotChangeIndex = !this._didWarnMust && SuggestAdapter._mustNotChangeDiff(_mustNotChange, resolvedItem); + if (typeof _mustNotChangeIndex === 'string') { this._logService.warn(`[${this._extensionId.value}] INVALID result from 'resolveCompletionItem', extension MUST NOT change any of: label, sortText, filterText, insertText, or textEdit`); - this._telemetry.$publicLog2('resolveCompletionItem/invalid', { extensionId: this._extensionId.value, kind: 'must' }); + this._telemetry.$publicLog2('resolveCompletionItem/invalid', { extensionId: this._extensionId.value, kind: 'must', index: _mustNotChangeIndex }); this._didWarnMust = true; } - if (!this._didWarnShould && _mayNotChange !== SuggestAdapter._mayNotChangeHash(resolvedItem)) { + let _mayNotChangeIndex = !this._didWarnShould && SuggestAdapter._mayNotChangeDiff(_mayNotChange, resolvedItem); + if (typeof _mayNotChangeIndex === 'string') { this._logService.info(`[${this._extensionId.value}] UNSAVE result from 'resolveCompletionItem', extension SHOULD NOT change any of: additionalTextEdits, or command`); - this._telemetry.$publicLog2('resolveCompletionItem/invalid', { extensionId: this._extensionId.value, kind: 'should' }); + this._telemetry.$publicLog2('resolveCompletionItem/invalid', { extensionId: this._extensionId.value, kind: 'should', index: _mayNotChangeIndex }); this._didWarnShould = true; } @@ -941,14 +945,43 @@ class SuggestAdapter { } private static _mustNotChangeHash(item: vscode.CompletionItem) { - const args = [item.label, item.sortText, item.filterText, item.insertText, item.range, item.range2]; - const res = JSON.stringify(args); + const res = JSON.stringify([item.label, item.sortText, item.filterText, item.insertText, item.range, item.range2]); return res; } + private static _mustNotChangeDiff(hash: string, item: vscode.CompletionItem): string | void { + const thisArr = [item.label, item.sortText, item.filterText, item.insertText, item.range, item.range2]; + const thisHash = JSON.stringify(thisArr); + if (hash === thisHash) { + return; + } + const arr = JSON.parse(hash); + for (let i = 0; i < 6; i++) { + if (JSON.stringify(arr[i] !== JSON.stringify(thisArr[i]))) { + return i.toString(); + } + } + return 'unknown'; + } + private static _mayNotChangeHash(item: vscode.CompletionItem) { return JSON.stringify([item.additionalTextEdits, item.command]); } + + private static _mayNotChangeDiff(hash: string, item: vscode.CompletionItem): string | void { + const thisArr = [item.additionalTextEdits, item.command]; + const thisHash = JSON.stringify(thisArr); + if (hash === thisHash) { + return; + } + const arr = JSON.parse(hash); + for (let i = 0; i < 6; i++) { + if (JSON.stringify(arr[i] !== JSON.stringify(thisArr[i]))) { + return i.toString(); + } + } + return 'unknown'; + } } class SignatureHelpAdapter { From 0fe69bd5298193a3bf0687a2347f81c7fcee1755 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 13 Jan 2020 12:42:45 +0100 Subject: [PATCH 183/843] TS semantic highlighting: check api version --- .../src/features/semanticTokens.ts | 8 ++++++++ extensions/typescript-language-features/src/utils/api.ts | 1 + 2 files changed, 9 insertions(+) diff --git a/extensions/typescript-language-features/src/features/semanticTokens.ts b/extensions/typescript-language-features/src/features/semanticTokens.ts index fefb8e2271f..f9f489321a2 100644 --- a/extensions/typescript-language-features/src/features/semanticTokens.ts +++ b/extensions/typescript-language-features/src/features/semanticTokens.ts @@ -6,8 +6,16 @@ import * as vscode from 'vscode'; import { ITypeScriptServiceClient, ExecConfig, ServerResponse } from '../typescriptService'; import * as Proto from '../protocol'; +import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import API from '../utils/api'; + +const minTypeScriptVersion = API.v370; export function register(selector: vscode.DocumentSelector, client: ITypeScriptServiceClient) { + return new VersionDependentRegistration(client, minTypeScriptVersion, () => { + const provider = new SemanticTokensProvider(client); + return vscode.languages.registerSemanticTokensProvider(selector, provider, provider.getLegend()); + }); const provider = new SemanticTokensProvider(client); return vscode.languages.registerSemanticTokensProvider(selector, provider, provider.getLegend()); } diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index 0fa41cb01a0..8f181f14b65 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -31,6 +31,7 @@ export default class API { public static readonly v340 = API.fromSimpleString('3.4.0'); public static readonly v345 = API.fromSimpleString('3.4.5'); public static readonly v350 = API.fromSimpleString('3.5.0'); + public static readonly v370 = API.fromSimpleString('3.7.0'); public static readonly v380 = API.fromSimpleString('3.8.0'); public static fromVersionString(versionString: string): API { From 4c9c6780c7a04c2992669cdb06f16042a4a4a8f4 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 13 Jan 2020 12:48:37 +0100 Subject: [PATCH 184/843] Fix #88541 --- src/vs/editor/contrib/parameterHints/parameterHints.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/parameterHints/parameterHints.css b/src/vs/editor/contrib/parameterHints/parameterHints.css index b8745f45d72..89bf6462e7f 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHints.css +++ b/src/vs/editor/contrib/parameterHints/parameterHints.css @@ -54,7 +54,7 @@ white-space: initial; } -.monaco-editor .parameter-hints-widget .docs .markdown-docs p code { +.monaco-editor .parameter-hints-widget .docs .markdown-docs code { font-family: var(--monaco-monospace-font); } From 224f983016eb313799f7c3b23e223dbbb596b329 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 Jan 2020 12:50:13 +0100 Subject: [PATCH 185/843] text files - drop debounced bulk events --- .../experiments/common/experimentService.ts | 12 ++-- .../browser/editors/fileEditorTracker.ts | 45 +++++++------ .../browser/languageSurveys.contribution.ts | 16 +++-- .../common/textFileEditorModelManager.ts | 44 +------------ .../services/textfile/common/textfiles.ts | 4 -- .../test/textFileEditorModelManager.test.ts | 63 +++---------------- 6 files changed, 55 insertions(+), 129 deletions(-) diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index f49e4ac868b..7f40caaa3ca 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -14,12 +14,13 @@ import { language } from 'vs/base/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; import { match } from 'vs/base/common/glob'; import { IRequestService, asJson } from 'vs/platform/request/common/request'; -import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, StateChange, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles'; import { CancellationToken } from 'vs/base/common/cancellation'; import { distinct } from 'vs/base/common/arrays'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { IProductService } from 'vs/platform/product/common/productService'; import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags'; +import { RunOnceWorker } from 'vs/base/common/async'; export const enum ExperimentState { Evaluating, @@ -402,11 +403,13 @@ export class ExperimentService extends Disposable implements IExperimentService return ExperimentState.Run; } - const onSaveHandler = this.textFileService.models.onModelsSaved(e => { + // Process model-save event every 250ms to reduce load + const onModelsSavedWorker = this._register(new RunOnceWorker(e => { const date = new Date().toDateString(); const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); if (latestExperimentState.state !== ExperimentState.Evaluating) { onSaveHandler.dispose(); + onModelsSavedWorker.dispose(); return; } e.forEach(async event => { @@ -444,8 +447,9 @@ export class ExperimentService extends Disposable implements IExperimentService this.fireRunExperiment(processedExperiment); } } - }); - this._register(onSaveHandler); + }, 250)); + + const onSaveHandler = this._register(this.textFileService.models.onModelSaved(e => onModelsSavedWorker.work(e))); return ExperimentState.Evaluating; }); } diff --git a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts index bebcb9fcf99..f5f9d80fa3b 100644 --- a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts @@ -7,7 +7,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { URI } from 'vs/base/common/uri'; import { IEditorViewState } from 'vs/editor/common/editorCommon'; import { toResource, SideBySideEditorInput, IWorkbenchEditorConfiguration, SideBySideEditor as SideBySideEditorChoice } from 'vs/workbench/common/editor'; -import { ITextFileService, TextFileModelChangeEvent, ModelState } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ModelState } from 'vs/workbench/services/textfile/common/textfiles'; import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileChangesEvent, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; @@ -26,6 +26,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { isEqualOrParent, joinPath } from 'vs/base/common/resources'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; +import { Schemas } from 'vs/base/common/network'; export class FileEditorTracker extends Disposable implements IWorkbenchContribution { @@ -60,9 +61,9 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut this._register(this.fileService.onFileChanges(e => this.onFileChanges(e))); // Ensure dirty text file and untitled models are always opened as editors - this._register(this.textFileService.models.onModelsDirty(e => this.ensureDirtyTextFilesAreOpened(e))); - this._register(this.textFileService.models.onModelsSaveError(e => this.ensureDirtyTextFilesAreOpened(e))); - this._register(this.untitledTextEditorService.onDidChangeDirty(e => this.ensureDirtyUntitledTextFilesAreOpenedWorker.work(e))); + this._register(this.textFileService.models.onModelDirty(e => this.ensureDirtyFilesAreOpenedWorker.work(e.resource))); + this._register(this.textFileService.models.onModelSaveError(e => this.ensureDirtyFilesAreOpenedWorker.work(e.resource))); + this._register(this.untitledTextEditorService.onDidChangeDirty(e => this.ensureDirtyFilesAreOpenedWorker.work(e))); // Out of workspace file watchers this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidVisibleEditorsChange())); @@ -277,22 +278,30 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut //#region Text File: Ensure every dirty text and untitled file is opened in an editor - private readonly ensureDirtyUntitledTextFilesAreOpenedWorker = this._register(new RunOnceWorker(units => this.ensureDirtyUntitledTextFilesAreOpened(units), 250)); + private readonly ensureDirtyFilesAreOpenedWorker = this._register(new RunOnceWorker(units => this.ensureDirtyFilesAreOpened(units), 250)); - private ensureDirtyTextFilesAreOpened(events: ReadonlyArray): void { - this.doEnsureDirtyFilesAreOpened(distinct(events.filter(({ resource }) => { - const model = this.textFileService.models.get(resource); - - return model?.hasState(ModelState.DIRTY) && // model must be dirty - !model.hasState(ModelState.PENDING_SAVE) && // model should not be saving currently - !this.editorService.isOpen({ resource }); // model is not currently opened as editor - }).map(event => event.resource), resource => resource.toString())); - } - - private ensureDirtyUntitledTextFilesAreOpened(resources: URI[]): void { + private ensureDirtyFilesAreOpened(resources: URI[]): void { this.doEnsureDirtyFilesAreOpened(distinct(resources.filter(resource => { - return this.untitledTextEditorService.isDirty(resource) && // untitled must be dirty - !this.editorService.isOpen({ resource }); // untitled is not currently opened as editor + if (resource.scheme === Schemas.untitled) { + if (!this.untitledTextEditorService.isDirty(resource)) { + return false; // untitled must be dirty + } + } else { + const model = this.textFileService.models.get(resource); + if (!model) { + return false; // only for text file models + } + + if (!model?.hasState(ModelState.DIRTY) || model.hasState(ModelState.PENDING_SAVE)) { + return false; // model must be dirty and not being saved + } + } + + if (this.editorService.isOpen({ resource })) { + return false; // model must not be opened already + } + + return true; }), resource => resource.toString())); } diff --git a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts index e25d5601d7c..a4242c6958d 100644 --- a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts @@ -13,12 +13,14 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { ISurveyData, IProductService } from 'vs/platform/product/common/productService'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; -import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, StateChange, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; import { platform } from 'vs/base/common/process'; +import { RunOnceWorker } from 'vs/base/common/async'; +import { Disposable } from 'vs/base/common/lifecycle'; -class LanguageSurvey { +class LanguageSurvey extends Disposable { constructor( data: ISurveyData, @@ -30,6 +32,8 @@ class LanguageSurvey { openerService: IOpenerService, productService: IProductService ) { + super(); + const SESSION_COUNT_KEY = `${data.surveyId}.sessionCount`; const LAST_SESSION_DATE_KEY = `${data.surveyId}.lastSessionDate`; const SKIP_VERSION_KEY = `${data.surveyId}.skipVersion`; @@ -45,7 +49,9 @@ class LanguageSurvey { const date = new Date().toDateString(); if (storageService.getNumber(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) < data.editCount) { - textFileService.models.onModelsSaved(e => { + + // Process model-save event every 250ms to reduce load + const onModelsSavedWorker = this._register(new RunOnceWorker(e => { e.forEach(event => { if (event.kind === StateChange.SAVED) { const model = modelService.getModel(event.resource); @@ -56,7 +62,9 @@ class LanguageSurvey { } } }); - }); + }, 250)); + + this._register(textFileService.models.onModelSaved(e => onModelsSavedWorker.work(e))); } const lastSessionDate = storageService.get(LAST_SESSION_DATE_KEY, StorageScope.GLOBAL, new Date(0).toDateString()); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 523f371578a..0db12b9d2fd 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle'; @@ -39,33 +39,6 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE private readonly _onModelOrphanedChanged = this._register(new Emitter()); readonly onModelOrphanedChanged = this._onModelOrphanedChanged.event; - private _onModelsDirty: Event> | undefined; - get onModelsDirty(): Event> { - if (!this._onModelsDirty) { - this._onModelsDirty = this.debounce(this.onModelDirty); - } - - return this._onModelsDirty; - } - - private _onModelsSaveError: Event> | undefined; - get onModelsSaveError(): Event> { - if (!this._onModelsSaveError) { - this._onModelsSaveError = this.debounce(this.onModelSaveError); - } - - return this._onModelsSaveError; - } - - private _onModelsSaved: Event> | undefined; - get onModelsSaved(): Event> { - if (!this._onModelsSaved) { - this._onModelsSaved = this.debounce(this.onModelSaved); - } - - return this._onModelsSaved; - } - private readonly mapResourceToDisposeListener = new ResourceMap(); private readonly mapResourceToStateChangeListener = new ResourceMap(); private readonly mapResourceToModelContentChangeListener = new ResourceMap(); @@ -116,21 +89,6 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE } } - private debounce(event: Event): Event { - return Event.debounce(event, (prev, cur) => { - if (!prev) { - prev = [cur]; - } else { - prev.push(cur); - } - return prev; - }, this.debounceDelay()); - } - - protected debounceDelay(): number { - return 250; - } - get(resource: URI): ITextFileEditorModel | undefined { return this.mapResourceToModel.get(resource); } diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index c5efc94245d..92056c53d84 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -387,10 +387,6 @@ export interface ITextFileEditorModelManager { readonly onModelSaved: Event; readonly onModelReverted: Event; - readonly onModelsDirty: Event; - readonly onModelsSaveError: Event; - readonly onModelsSaved: Event; - get(resource: URI): ITextFileEditorModel | undefined; getAll(resource?: URI): ITextFileEditorModel[]; diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts index 8c9e2e98670..da4fbc06caf 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -11,17 +11,9 @@ import { workbenchInstantiationService, TestFileService } from 'vs/workbench/tes import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { timeout } from 'vs/base/common/async'; import { toResource } from 'vs/base/test/common/utils'; import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; -export class TestTextFileEditorModelManager extends TextFileEditorModelManager { - - protected debounceDelay(): number { - return 10; - } -} - class ServiceAccessor { constructor( @IFileService public fileService: TestFileService, @@ -41,7 +33,7 @@ suite('Files - TextFileEditorModelManager', () => { }); test('add, remove, clear, get, getAll', function () { - const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + const manager: TextFileEditorModelManager = instantiationService.createInstance(TextFileEditorModelManager); const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8', undefined); const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8', undefined); @@ -96,7 +88,7 @@ suite('Files - TextFileEditorModelManager', () => { }); test('loadOrCreate', async () => { - const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + const manager: TextFileEditorModelManager = instantiationService.createInstance(TextFileEditorModelManager); const resource = URI.file('/test.html'); const encoding = 'utf8'; @@ -116,7 +108,7 @@ suite('Files - TextFileEditorModelManager', () => { }); test('removed from cache when model disposed', function () { - const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + const manager: TextFileEditorModelManager = instantiationService.createInstance(TextFileEditorModelManager); const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8', undefined); const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8', undefined); @@ -136,7 +128,7 @@ suite('Files - TextFileEditorModelManager', () => { }); test('events', async function () { - const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + const manager: TextFileEditorModelManager = instantiationService.createInstance(TextFileEditorModelManager); const resource1 = toResource.call(this, '/path/index.txt'); const resource2 = toResource.call(this, '/path/other.txt'); @@ -208,49 +200,8 @@ suite('Files - TextFileEditorModelManager', () => { assert.ok(!accessor.modelService.getModel(resource2)); }); - test('events debounced', async function () { - const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); - - const resource1 = toResource.call(this, '/path/index.txt'); - const resource2 = toResource.call(this, '/path/other.txt'); - - let dirtyCounter = 0; - let savedCounter = 0; - - manager.onModelsDirty(e => { - dirtyCounter += e.length; - assert.equal(e[0].resource.toString(), resource1.toString()); - }); - - manager.onModelsSaved(e => { - savedCounter += e.length; - assert.equal(e[0].resource.toString(), resource1.toString()); - }); - - const model1 = await manager.loadOrCreate(resource1, { encoding: 'utf8' }); - const model2 = await manager.loadOrCreate(resource2, { encoding: 'utf8' }); - model1.textEditorModel!.setValue('changed'); - model1.updatePreferredEncoding('utf16'); - - await model1.revert(); - model1.textEditorModel!.setValue('changed again'); - - await model1.save(); - model1.dispose(); - model2.dispose(); - - await model1.revert(); - await timeout(20); - assert.equal(dirtyCounter, 2); - assert.equal(savedCounter, 1); - model1.dispose(); - model2.dispose(); - assert.ok(!accessor.modelService.getModel(resource1)); - assert.ok(!accessor.modelService.getModel(resource2)); - }); - test('disposing model takes it out of the manager', async function () { - const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + const manager: TextFileEditorModelManager = instantiationService.createInstance(TextFileEditorModelManager); const resource = toResource.call(this, '/path/index_something.txt'); @@ -262,7 +213,7 @@ suite('Files - TextFileEditorModelManager', () => { }); test('dispose prevents dirty model from getting disposed', async function () { - const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + const manager: TextFileEditorModelManager = instantiationService.createInstance(TextFileEditorModelManager); const resource = toResource.call(this, '/path/index_something.txt'); @@ -282,7 +233,7 @@ suite('Files - TextFileEditorModelManager', () => { id: mode, }); - const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + const manager: TextFileEditorModelManager = instantiationService.createInstance(TextFileEditorModelManager); const resource = toResource.call(this, '/path/index_something.txt'); From 9918d87cb69befa421cd2019ac51f74cc644c233 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 13 Jan 2020 13:01:11 +0100 Subject: [PATCH 186/843] Fix #88402 --- .../api/browser/viewsExtensionPoint.ts | 5 +-- .../browser/parts/views/viewPaneContainer.ts | 2 +- .../browser/workbench.contribution.ts | 4 +-- src/vs/workbench/common/views.ts | 5 +-- .../bulkEdit/browser/bulkEdit.contribution.ts | 11 ++++--- .../debug/browser/debug.contribution.ts | 15 ++++----- .../browser/extensions.contribution.ts | 2 +- .../extensions/browser/extensionsViewlet.ts | 31 ++++++++++--------- .../contrib/files/browser/explorerViewlet.ts | 9 +++--- .../markers/browser/markers.contribution.ts | 5 +-- .../outline/browser/outline.contribution.ts | 3 +- .../contrib/remote/browser/remote.ts | 7 +++-- .../contrib/remote/browser/tunnelView.ts | 5 +-- .../workbench/contrib/scm/browser/mainPane.ts | 5 +-- .../contrib/scm/browser/repositoryPane.ts | 5 +-- .../contrib/scm/browser/scm.contribution.ts | 3 +- .../search/browser/search.contribution.ts | 4 +-- .../test/browser/parts/views/views.test.ts | 3 +- 18 files changed, 69 insertions(+), 55 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index e721bb24267..65bb4afb1dd 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -36,6 +36,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; export interface IUserFriendlyViewsContainerDescriptor { id: string; @@ -332,7 +333,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { viewContainer = this.viewContainersRegistry.registerViewContainer({ id, name: title, extensionId, - ctorDescriptor: { ctor: CustomViewPaneContainer }, + ctorDescriptor: new SyncDescriptor(CustomViewPaneContainer), hideIfEmpty: true, icon, }, ViewContainerLocation.Sidebar); @@ -416,7 +417,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { const viewDescriptor = { id: item.id, name: item.name, - ctorDescriptor: { ctor: CustomTreeViewPane }, + ctorDescriptor: new SyncDescriptor(CustomTreeViewPane), when: ContextKeyExpr.deserialize(item.when), canToggleVisibility: true, collapsed: this.showCollapsed(container), diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index fc7f3528c07..95095030c48 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -492,7 +492,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { } protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewPane { - return (this.instantiationService as any).createInstance(viewDescriptor.ctorDescriptor.ctor, ...(viewDescriptor.ctorDescriptor.arguments || []), options) as ViewPane; + return (this.instantiationService as any).createInstance(viewDescriptor.ctorDescriptor.ctor, ...(viewDescriptor.ctorDescriptor.staticArguments || []), options) as ViewPane; } getView(id: string): ViewPane | undefined { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index c14125bea17..f2a68f351d3 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -402,7 +402,7 @@ import { URI } from 'vs/base/common/uri'; @IExtensionService extensionService: IExtensionService, @IWorkspaceContextService contextService: IWorkspaceContextService ) { - super(viewContainer.id, (instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.arguments || [])), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + super(viewContainer.id, (instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); } } Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create( @@ -429,7 +429,7 @@ import { URI } from 'vs/base/common/uri'; @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService ) { - super(viewContainer.id, (instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.arguments || [])), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService); + super(viewContainer.id, (instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService); } } const viewletDescriptor = ViewletDescriptor.create( diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 15ee4827de8..174df04dff6 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -19,6 +19,7 @@ import { IAction } from 'vs/base/common/actions'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { flatten } from 'vs/base/common/arrays'; import { IViewPaneContainer } from 'vs/workbench/common/viewPaneContainer'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test'; export const FocusedViewContext = new RawContextKey('focusedView', ''); @@ -39,7 +40,7 @@ export interface IViewContainerDescriptor { readonly name: string; - readonly ctorDescriptor: { ctor: new (...args: any[]) => IViewPaneContainer, arguments?: any[] }; + readonly ctorDescriptor: SyncDescriptor; readonly icon?: string | URI; @@ -166,7 +167,7 @@ export interface IViewDescriptor { readonly name: string; - readonly ctorDescriptor: { ctor: any, arguments?: any[] }; + readonly ctorDescriptor: SyncDescriptor; readonly when?: ContextKeyExpr; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index 19f870dec79..2a705730bae 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -21,6 +21,7 @@ import { BulkEditPreviewProvider } from 'vs/workbench/contrib/bulkEdit/browser/b import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; function getBulkEditPane(panelService: IPanelService): BulkEditPane | undefined { let view: ViewPane | undefined; @@ -128,16 +129,16 @@ const container = Registry.as(ViewContainerExtensions.V id: BulkEditPane.ID, name: localize('panel', "Refactor Preview"), hideIfEmpty: true, - ctorDescriptor: { - ctor: ViewPaneContainer, - arguments: [BulkEditPane.ID, BulkEditPane.ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }] - } + ctorDescriptor: new SyncDescriptor( + ViewPaneContainer, + [BulkEditPane.ID, BulkEditPane.ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }] + ) }, ViewContainerLocation.Panel); Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ id: BulkEditPane.ID, name: localize('panel', "Refactor Preview"), when: BulkEditPreviewContribution.ctxEnabled, - ctorDescriptor: { ctor: BulkEditPane }, + ctorDescriptor: new SyncDescriptor(BulkEditPane), }], container); diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 7626a1780ce..3874102dd89 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -52,6 +52,7 @@ import { DebugViewPaneContainer } from 'vs/workbench/contrib/debug/browser/debug import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { CallStackEditorContribution } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution'; import { BreakpointEditorContribution } from 'vs/workbench/contrib/debug/browser/breakpointEditorContribution'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; class OpenDebugViewletAction extends ShowViewletAction { public static readonly ID = VIEWLET_ID; @@ -85,7 +86,7 @@ class OpenDebugPanelAction extends TogglePanelAction { const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, name: nls.localize('runAndDebug', "Run and Debug"), - ctorDescriptor: { ctor: DebugViewPaneContainer }, + ctorDescriptor: new SyncDescriptor(DebugViewPaneContainer), icon: 'codicon-debug-alt', order: 3 }, ViewContainerLocation.Sidebar); @@ -109,12 +110,12 @@ Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor // Register default debug views const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); -viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctorDescriptor: { ctor: VariablesView }, order: 10, weight: 40, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); -viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctorDescriptor: { ctor: WatchExpressionsView }, order: 20, weight: 10, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); -viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctorDescriptor: { ctor: CallStackView }, order: 30, weight: 30, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); -viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctorDescriptor: { ctor: BreakpointsView }, order: 40, weight: 20, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); -viewsRegistry.registerViews([{ id: StartView.ID, name: StartView.LABEL, ctorDescriptor: { ctor: StartView }, order: 10, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer); -viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), ctorDescriptor: { ctor: LoadedScriptsView }, order: 35, weight: 5, canToggleVisibility: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); +viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); +viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); +viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); +viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); +viewsRegistry.registerViews([{ id: StartView.ID, name: StartView.LABEL, ctorDescriptor: new SyncDescriptor(StartView), order: 10, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer); +viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); registerCommands(); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 80f565cbd2d..6550640590a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -82,7 +82,7 @@ Registry.as(ViewContainerExtensions.ViewContainersRegis { id: VIEWLET_ID, name: localize('extensions', "Extensions"), - ctorDescriptor: { ctor: ExtensionsViewPaneContainer }, + ctorDescriptor: new SyncDescriptor(ExtensionsViewPaneContainer), icon: 'codicon-extensions', order: 4 }, ViewContainerLocation.Sidebar); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index eb05d9ef19a..9accf3727af 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -57,6 +57,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { RemoteNameContext } from 'vs/workbench/browser/contextkeys'; import { ILabelService } from 'vs/platform/label/common/label'; import { MementoObject } from 'vs/workbench/common/memento'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; const NonEmptyWorkspaceContext = new RawContextKey('nonEmptyWorkspace', false); const DefaultViewsContext = new RawContextKey('defaultExtensionViews', true); @@ -128,7 +129,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctorDescriptor: { ctor: ExtensionsListView }, + ctorDescriptor: new SyncDescriptor(ExtensionsListView), when: ContextKeyExpr.and(ContextKeyExpr.has('searchMarketplaceExtensions')), weight: 100 }; @@ -141,7 +142,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctorDescriptor: { ctor: EnabledExtensionsView }, + ctorDescriptor: new SyncDescriptor(EnabledExtensionsView), when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.isEqualTo('')), weight: 40, canToggleVisibility: true, @@ -156,7 +157,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctorDescriptor: { ctor: DisabledExtensionsView }, + ctorDescriptor: new SyncDescriptor(DisabledExtensionsView), when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.isEqualTo('')), weight: 10, canToggleVisibility: true, @@ -172,7 +173,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctorDescriptor: { ctor: ExtensionsListView }, + ctorDescriptor: new SyncDescriptor(ExtensionsListView), when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')), weight: 60, order: 1 @@ -193,19 +194,19 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return [{ id: `extensions.${server.authority}.installed`, get name() { return getInstalledViewName(); }, - ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())] }, + ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')), weight: 100 }, { id: `extensions.${server.authority}.outdated`, get name() { return getOutdatedViewName(); }, - ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server, EventOf.map(onDidChangeServerLabel, () => getOutdatedViewName())] }, + ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getOutdatedViewName())]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchOutdatedExtensions')), weight: 100 }, { id: `extensions.${server.authority}.default`, get name() { return getInstalledViewName(); }, - ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())] }, + ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())]), when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.notEqualsTo('')), weight: 40, order: 1 @@ -220,7 +221,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctorDescriptor: { ctor: DefaultRecommendedExtensionsView }, + ctorDescriptor: new SyncDescriptor(DefaultRecommendedExtensionsView), when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('defaultRecommendedExtensions')), weight: 40, order: 2, @@ -235,7 +236,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctorDescriptor: { ctor: RecommendedExtensionsView }, + ctorDescriptor: new SyncDescriptor(RecommendedExtensionsView), when: ContextKeyExpr.has('recommendedExtensions'), weight: 50, order: 2 @@ -249,7 +250,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctorDescriptor: { ctor: WorkspaceRecommendedExtensionsView }, + ctorDescriptor: new SyncDescriptor(WorkspaceRecommendedExtensionsView), when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), ContextKeyExpr.has('nonEmptyWorkspace')), weight: 50, order: 1 @@ -261,7 +262,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctorDescriptor: { ctor: EnabledExtensionsView }, + ctorDescriptor: new SyncDescriptor(EnabledExtensionsView), when: ContextKeyExpr.and(ContextKeyExpr.has('searchEnabledExtensions')), weight: 40, order: 1 @@ -273,7 +274,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctorDescriptor: { ctor: DisabledExtensionsView }, + ctorDescriptor: new SyncDescriptor(DisabledExtensionsView), when: ContextKeyExpr.and(ContextKeyExpr.has('searchDisabledExtensions')), weight: 10, order: 3, @@ -286,7 +287,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctorDescriptor: { ctor: BuiltInExtensionsView }, + ctorDescriptor: new SyncDescriptor(BuiltInExtensionsView), when: ContextKeyExpr.has('searchBuiltInExtensions'), weight: 100 }; @@ -297,7 +298,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctorDescriptor: { ctor: BuiltInThemesExtensionsView }, + ctorDescriptor: new SyncDescriptor(BuiltInThemesExtensionsView), when: ContextKeyExpr.has('searchBuiltInExtensions'), weight: 100 }; @@ -308,7 +309,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctorDescriptor: { ctor: BuiltInBasicsExtensionsView }, + ctorDescriptor: new SyncDescriptor(BuiltInBasicsExtensionsView), when: ContextKeyExpr.has('searchBuiltInExtensions'), weight: 100 }; diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index 94d591094b6..82d603d63fb 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -33,6 +33,7 @@ import { ViewPane, ViewPaneContainer } from 'vs/workbench/browser/parts/views/vi import { KeyChord, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; export class ExplorerViewletViewsContribution extends Disposable implements IWorkbenchContribution { @@ -103,7 +104,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: OpenEditorsView.ID, name: OpenEditorsView.NAME, - ctorDescriptor: { ctor: OpenEditorsView }, + ctorDescriptor: new SyncDescriptor(OpenEditorsView), order: 0, when: OpenEditorsVisibleContext, canToggleVisibility: true, @@ -118,7 +119,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: EmptyView.ID, name: EmptyView.NAME, - ctorDescriptor: { ctor: EmptyView }, + ctorDescriptor: new SyncDescriptor(EmptyView), order: 1, canToggleVisibility: true, }; @@ -128,7 +129,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: ExplorerView.ID, name: localize('folders', "Folders"), - ctorDescriptor: { ctor: ExplorerView }, + ctorDescriptor: new SyncDescriptor(ExplorerView), order: 1, canToggleVisibility: false }; @@ -247,7 +248,7 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer { export const VIEW_CONTAINER: ViewContainer = Registry.as(Extensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, name: localize('explore', "Explorer"), - ctorDescriptor: { ctor: ExplorerViewPaneContainer }, + ctorDescriptor: new SyncDescriptor(ExplorerViewPaneContainer), icon: 'codicon-files', order: 0 }, ViewContainerLocation.Sidebar); diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index 1baf6e1c4f9..bc3a4821115 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -32,6 +32,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewsRegistry } from 'vs/workbench/common/views'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; registerSingleton(IMarkersWorkbenchService, MarkersWorkbenchService, false); @@ -110,7 +111,7 @@ class ToggleMarkersPanelAction extends TogglePanelAction { const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: Constants.MARKERS_PANEL_ID, name: Messages.MARKERS_PANEL_TITLE_PROBLEMS, - ctorDescriptor: { ctor: ViewPaneContainer, arguments: [Constants.MARKERS_PANEL_ID, Constants.MARKERS_PANEL_STORAGE_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }] }, + ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [Constants.MARKERS_PANEL_ID, Constants.MARKERS_PANEL_STORAGE_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), focusCommand: { id: ToggleMarkersPanelAction.ID, keybindings: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_M @@ -122,7 +123,7 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews id: Constants.MARKERS_VIEW_ID, name: Messages.MARKERS_PANEL_TITLE_PROBLEMS, canToggleVisibility: false, - ctorDescriptor: { ctor: MarkersView }, + ctorDescriptor: new SyncDescriptor(MarkersView), }], VIEW_CONTAINER); // workbench diff --git a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts index d3d712fe18a..d95ee1360d0 100644 --- a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts +++ b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts @@ -10,13 +10,14 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { OutlineConfigKeys, OutlineViewId } from 'vs/editor/contrib/documentSymbols/outline'; import { VIEW_CONTAINER } from 'vs/workbench/contrib/files/browser/explorerViewlet'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; // import './outlineNavigation'; const _outlineDesc = { id: OutlineViewId, name: localize('name', "Outline"), - ctorDescriptor: { ctor: OutlinePane }, + ctorDescriptor: new SyncDescriptor(OutlinePane), canToggleVisibility: true, hideByDefault: false, collapsed: true, diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 3edc8581464..27ce85e8cb3 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -54,6 +54,7 @@ import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Event } from 'vs/base/common/event'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; export interface HelpInformation { extensionDescription: IExtensionDescription; @@ -406,13 +407,13 @@ class HelpPanel extends ViewPane { class HelpPanelDescriptor implements IViewDescriptor { readonly id = HelpPanel.ID; readonly name = HelpPanel.TITLE; - readonly ctorDescriptor: { ctor: any, arguments?: any[] }; + readonly ctorDescriptor: SyncDescriptor; readonly canToggleVisibility = true; readonly hideByDefault = false; readonly workspace = true; constructor(viewModel: IViewModel) { - this.ctorDescriptor = { ctor: HelpPanel, arguments: [viewModel] }; + this.ctorDescriptor = new SyncDescriptor(HelpPanel, [viewModel]); } } @@ -521,7 +522,7 @@ Registry.as(Extensions.ViewContainersRegistry).register { id: VIEWLET_ID, name: nls.localize('remote.explorer', "Remote Explorer"), - ctorDescriptor: { ctor: RemoteViewPaneContainer }, + ctorDescriptor: new SyncDescriptor(RemoteViewPaneContainer), hideIfEmpty: true, viewOrderDelegate: { getOrder: (group?: string) => { diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index ec5fedf10f9..c88132b8b2c 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -38,6 +38,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { URI } from 'vs/base/common/uri'; import { RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; export const forwardedPortsViewEnabled = new RawContextKey('forwardedPortsViewEnabled', false); @@ -574,7 +575,7 @@ export class TunnelPanel extends ViewPane { export class TunnelPanelDescriptor implements IViewDescriptor { readonly id = TunnelPanel.ID; readonly name = TunnelPanel.TITLE; - readonly ctorDescriptor: { ctor: any, arguments?: any[] }; + readonly ctorDescriptor: SyncDescriptor; readonly canToggleVisibility = true; readonly hideByDefault = false; readonly workspace = true; @@ -582,7 +583,7 @@ export class TunnelPanelDescriptor implements IViewDescriptor { readonly remoteAuthority?: string | string[]; constructor(viewModel: ITunnelViewModel, environmentService: IWorkbenchEnvironmentService) { - this.ctorDescriptor = { ctor: TunnelPanel, arguments: [viewModel] }; + this.ctorDescriptor = new SyncDescriptor(TunnelPanel, [viewModel]); this.remoteAuthority = environmentService.configuration.remoteAuthority ? environmentService.configuration.remoteAuthority.split('+')[0] : undefined; } } diff --git a/src/vs/workbench/contrib/scm/browser/mainPane.ts b/src/vs/workbench/contrib/scm/browser/mainPane.ts index d5ec661f87b..f44e13e5e9c 100644 --- a/src/vs/workbench/contrib/scm/browser/mainPane.ts +++ b/src/vs/workbench/contrib/scm/browser/mainPane.ts @@ -31,6 +31,7 @@ import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewDescriptor } from 'vs/workbench/common/views'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; export interface ISpliceEvent { index: number; @@ -320,7 +321,7 @@ export class MainPaneDescriptor implements IViewDescriptor { readonly id = MainPane.ID; readonly name = MainPane.TITLE; - readonly ctorDescriptor: { ctor: any, arguments?: any[] }; + readonly ctorDescriptor: SyncDescriptor; readonly canToggleVisibility = true; readonly hideByDefault = false; readonly order = -1000; @@ -328,6 +329,6 @@ export class MainPaneDescriptor implements IViewDescriptor { readonly when = ContextKeyExpr.or(ContextKeyExpr.equals('config.scm.alwaysShowProviders', true), ContextKeyExpr.and(ContextKeyExpr.notEquals('scm.providerCount', 0), ContextKeyExpr.notEquals('scm.providerCount', 1))); constructor(viewModel: IViewModel) { - this.ctorDescriptor = { ctor: MainPane, arguments: [viewModel] }; + this.ctorDescriptor = new SyncDescriptor(MainPane, [viewModel]); } } diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index ac0040072c6..95626a88b2c 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -54,6 +54,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { Hasher } from 'vs/base/common/hash'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; type TreeElement = ISCMResourceGroup | IResourceNode | ISCMResource; @@ -957,7 +958,7 @@ export class RepositoryViewDescriptor implements IViewDescriptor { readonly id: string; readonly name: string; - readonly ctorDescriptor: { ctor: any, arguments?: any[] }; + readonly ctorDescriptor: SyncDescriptor; readonly canToggleVisibility = true; readonly order = -500; readonly workspace = true; @@ -970,6 +971,6 @@ export class RepositoryViewDescriptor implements IViewDescriptor { this.id = `scm:repository:${hasher.value}`; this.name = repository.provider.rootUri ? basename(repository.provider.rootUri) : repository.provider.label; - this.ctorDescriptor = { ctor: RepositoryPane, arguments: [repository] }; + this.ctorDescriptor = new SyncDescriptor(RepositoryPane, [repository]); } } diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 73e1959661d..fd371ac79b0 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -25,6 +25,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { SCMService } from 'vs/workbench/contrib/scm/common/scmService'; import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; import { SCMViewPaneContainer } from 'vs/workbench/contrib/scm/browser/scmViewlet'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; class OpenSCMViewletAction extends ShowViewletAction { @@ -42,7 +43,7 @@ Registry.as(WorkbenchExtensions.Workbench) Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, name: localize('source control', "Source Control"), - ctorDescriptor: { ctor: SCMViewPaneContainer }, + ctorDescriptor: new SyncDescriptor(SCMViewPaneContainer), icon: 'codicon-source-control', order: 2 }, ViewContainerLocation.Sidebar); diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 3a3383efb8e..3e698a8a74c 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -512,7 +512,7 @@ class ShowAllSymbolsAction extends Action { const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, name: nls.localize('name', "Search"), - ctorDescriptor: { ctor: SearchViewPaneContainer }, + ctorDescriptor: new SyncDescriptor(SearchViewPaneContainer), hideIfEmpty: true, icon: 'codicon-search', order: 1 @@ -550,7 +550,7 @@ class RegisterSearchViewContribution implements IWorkbenchContribution { } } else { Registry.as(PanelExtensions.Panels).deregisterPanel(PANEL_ID); - viewsRegistry.registerViews([{ id: VIEW_ID, name: nls.localize('search', "Search"), ctorDescriptor: { ctor: SearchView, arguments: [SearchViewPosition.SideBar] }, canToggleVisibility: false }], viewContainer); + viewsRegistry.registerViews([{ id: VIEW_ID, name: nls.localize('search', "Search"), ctorDescriptor: new SyncDescriptor(SearchView, [SearchViewPosition.SideBar]), canToggleVisibility: false }], viewContainer); if (open) { viewletService.openViewlet(VIEWLET_ID); } diff --git a/src/vs/workbench/test/browser/parts/views/views.test.ts b/src/vs/workbench/test/browser/parts/views/views.test.ts index 794ff35443b..a5244e4de17 100644 --- a/src/vs/workbench/test/browser/parts/views/views.test.ts +++ b/src/vs/workbench/test/browser/parts/views/views.test.ts @@ -14,8 +14,9 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import sinon = require('sinon'); +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: { ctor: {} } }, ViewContainerLocation.Sidebar); +const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const ViewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); class ViewDescriptorSequence { From 238617a847b378eeefbd95fc2e9cd585737837c3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 Jan 2020 13:02:41 +0100 Subject: [PATCH 187/843] debt - drop TextFileModelChangeEvent --- .../api/browser/mainThreadDocuments.ts | 24 +++++++------- .../browser/parts/editor/editorStatus.ts | 2 +- .../experiments/common/experimentService.ts | 13 ++++---- .../browser/editors/fileEditorTracker.ts | 6 ++-- .../editors/textFileSaveErrorHandler.ts | 4 +-- .../files/common/editors/fileEditorInput.ts | 20 +++++------ .../browser/languageSurveys.contribution.ts | 20 +++++------ .../browser/telemetry.contribution.ts | 18 +++++----- .../common/textFileEditorModelManager.ts | 33 +++++++++---------- .../services/textfile/common/textfiles.ts | 30 ++++------------- .../test/textFileEditorModelManager.test.ts | 20 +++++------ 11 files changed, 85 insertions(+), 105 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDocuments.ts b/src/vs/workbench/api/browser/mainThreadDocuments.ts index c54c2613028..3568bd30704 100644 --- a/src/vs/workbench/api/browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/browser/mainThreadDocuments.ts @@ -15,7 +15,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors'; import { ExtHostContext, ExtHostDocumentsShape, IExtHostContext, MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol'; import { ITextEditorModel } from 'vs/workbench/common/editor'; -import { ITextFileService, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { toLocalResource } from 'vs/base/common/resources'; @@ -104,19 +104,19 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { this._toDispose.add(this._modelReferenceCollection); this._toDispose.add(modelService.onModelModeChanged(this._onModelModeChanged, this)); - this._toDispose.add(textFileService.models.onModelSaved(e => { - if (this._shouldHandleFileEvent(e)) { - this._proxy.$acceptModelSaved(e.resource); + this._toDispose.add(textFileService.models.onModelSaved(m => { + if (this._shouldHandleFileEvent(m.resource)) { + this._proxy.$acceptModelSaved(m.resource); } })); - this._toDispose.add(textFileService.models.onModelReverted(e => { - if (this._shouldHandleFileEvent(e)) { - this._proxy.$acceptDirtyStateChanged(e.resource, false); + this._toDispose.add(textFileService.models.onModelReverted(m => { + if (this._shouldHandleFileEvent(m.resource)) { + this._proxy.$acceptDirtyStateChanged(m.resource, false); } })); - this._toDispose.add(textFileService.models.onModelDirty(e => { - if (this._shouldHandleFileEvent(e)) { - this._proxy.$acceptDirtyStateChanged(e.resource, true); + this._toDispose.add(textFileService.models.onModelDirty(m => { + if (this._shouldHandleFileEvent(m.resource)) { + this._proxy.$acceptDirtyStateChanged(m.resource, true); } })); @@ -131,8 +131,8 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { this._toDispose.dispose(); } - private _shouldHandleFileEvent(e: TextFileModelChangeEvent): boolean { - const model = this._modelService.getModel(e.resource); + private _shouldHandleFileEvent(resource: URI): boolean { + const model = this._modelService.getModel(resource); return !!model && shouldSynchronizeModel(model); } diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index a8909037e51..d832143cc04 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -314,7 +314,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { private registerListeners(): void { this._register(this.editorService.onDidActiveEditorChange(() => this.updateStatusBar())); this._register(this.untitledTextEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r))); - this._register(this.textFileService.models.onModelEncodingChanged(e => this.onResourceEncodingChange((e.resource)))); + this._register(this.textFileService.models.onModelEncodingChanged(m => this.onResourceEncodingChange((m.resource)))); this._register(TabFocus.onDidChangeTabFocus(e => this.onTabFocusModeChange())); } diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index 7f40caaa3ca..f0eccb33be3 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -14,7 +14,7 @@ import { language } from 'vs/base/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; import { match } from 'vs/base/common/glob'; import { IRequestService, asJson } from 'vs/platform/request/common/request'; -import { ITextFileService, StateChange, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { CancellationToken } from 'vs/base/common/cancellation'; import { distinct } from 'vs/base/common/arrays'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; @@ -404,7 +404,7 @@ export class ExperimentService extends Disposable implements IExperimentService } // Process model-save event every 250ms to reduce load - const onModelsSavedWorker = this._register(new RunOnceWorker(e => { + const onModelsSavedWorker = this._register(new RunOnceWorker(models => { const date = new Date().toDateString(); const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); if (latestExperimentState.state !== ExperimentState.Evaluating) { @@ -412,9 +412,8 @@ export class ExperimentService extends Disposable implements IExperimentService onModelsSavedWorker.dispose(); return; } - e.forEach(async event => { - if (event.kind !== StateChange.SAVED - || latestExperimentState.state !== ExperimentState.Evaluating + models.forEach(async model => { + if (latestExperimentState.state !== ExperimentState.Evaluating || date === latestExperimentState.lastEditedDate || (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) ) { @@ -424,7 +423,7 @@ export class ExperimentService extends Disposable implements IExperimentService let workspaceCheck = true; if (typeof fileEdits.filePathPattern === 'string') { - filePathCheck = match(fileEdits.filePathPattern, event.resource.fsPath); + filePathCheck = match(fileEdits.filePathPattern, model.resource.fsPath); } if (Array.isArray(fileEdits.workspaceIncludes) && fileEdits.workspaceIncludes.length) { const tags = await this.workspaceTagsService.getTags(); @@ -449,7 +448,7 @@ export class ExperimentService extends Disposable implements IExperimentService } }, 250)); - const onSaveHandler = this._register(this.textFileService.models.onModelSaved(e => onModelsSavedWorker.work(e))); + const onSaveHandler = this._register(this.textFileService.models.onModelSaved(m => onModelsSavedWorker.work(m))); return ExperimentState.Evaluating; }); } diff --git a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts index f5f9d80fa3b..183bae2faa9 100644 --- a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts @@ -61,9 +61,9 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut this._register(this.fileService.onFileChanges(e => this.onFileChanges(e))); // Ensure dirty text file and untitled models are always opened as editors - this._register(this.textFileService.models.onModelDirty(e => this.ensureDirtyFilesAreOpenedWorker.work(e.resource))); - this._register(this.textFileService.models.onModelSaveError(e => this.ensureDirtyFilesAreOpenedWorker.work(e.resource))); - this._register(this.untitledTextEditorService.onDidChangeDirty(e => this.ensureDirtyFilesAreOpenedWorker.work(e))); + this._register(this.textFileService.models.onModelDirty(m => this.ensureDirtyFilesAreOpenedWorker.work(m.resource))); + this._register(this.textFileService.models.onModelSaveError(m => this.ensureDirtyFilesAreOpenedWorker.work(m.resource))); + this._register(this.untitledTextEditorService.onDidChangeDirty(r => this.ensureDirtyFilesAreOpenedWorker.work(r))); // Out of workspace file watchers this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidVisibleEditorsChange())); diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index cebc296f377..e94d9a4d23d 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -71,8 +71,8 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa } private registerListeners(): void { - this._register(this.textFileService.models.onModelSaved(e => this.onFileSavedOrReverted(e.resource))); - this._register(this.textFileService.models.onModelReverted(e => this.onFileSavedOrReverted(e.resource))); + this._register(this.textFileService.models.onModelSaved(m => this.onFileSavedOrReverted(m.resource))); + this._register(this.textFileService.models.onModelReverted(m => this.onFileSavedOrReverted(m.resource))); this._register(this.editorService.onDidActiveEditorChange(() => this.onActiveEditorChanged())); } diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 799f816c760..f6f2db70292 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -11,7 +11,7 @@ import { EncodingMode, IFileEditorInput, ITextEditorModel, Verbosity, TextEditor import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { FileOperationError, FileOperationResult, IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; -import { ITextFileService, ModelState, TextFileModelChangeEvent, LoadReason, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ModelState, LoadReason, TextFileOperationError, TextFileOperationResult, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IReference } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -70,25 +70,25 @@ export class FileEditorInput extends TextEditorInput implements IFileEditorInput private registerListeners(): void { // Dirty changes - this._register(this.textFileService.models.onModelDirty(e => this.onDirtyStateChange(e))); - this._register(this.textFileService.models.onModelSaveError(e => this.onDirtyStateChange(e))); - this._register(this.textFileService.models.onModelSaved(e => this.onDirtyStateChange(e))); - this._register(this.textFileService.models.onModelReverted(e => this.onDirtyStateChange(e))); + this._register(this.textFileService.models.onModelDirty(m => this.onDirtyStateChange(m))); + this._register(this.textFileService.models.onModelSaveError(m => this.onDirtyStateChange(m))); + this._register(this.textFileService.models.onModelSaved(m => this.onDirtyStateChange(m))); + this._register(this.textFileService.models.onModelReverted(m => this.onDirtyStateChange(m))); // Label changes this._register(this.labelService.onDidChangeFormatters(() => FileEditorInput.MEMOIZER.clear())); this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(() => FileEditorInput.MEMOIZER.clear())); - this._register(this.textFileService.models.onModelOrphanedChanged(e => this.onModelOrphanedChanged(e))); + this._register(this.textFileService.models.onModelOrphanedChanged(model => this.onModelOrphanedChanged(model))); } - private onDirtyStateChange(e: TextFileModelChangeEvent): void { - if (e.resource.toString() === this.resource.toString()) { + private onDirtyStateChange(model: ITextFileEditorModel): void { + if (model.resource.toString() === this.resource.toString()) { this._onDidChangeDirty.fire(); } } - private onModelOrphanedChanged(e: TextFileModelChangeEvent): void { - if (e.resource.toString() === this.resource.toString()) { + private onModelOrphanedChanged(model: ITextFileEditorModel): void { + if (model.resource.toString() === this.resource.toString()) { FileEditorInput.MEMOIZER.clear(); this._onDidChangeLabel.fire(); } diff --git a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts index a4242c6958d..738a131dd18 100644 --- a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts @@ -13,7 +13,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { ISurveyData, IProductService } from 'vs/platform/product/common/productService'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; -import { ITextFileService, StateChange, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; import { platform } from 'vs/base/common/process'; @@ -51,20 +51,18 @@ class LanguageSurvey extends Disposable { if (storageService.getNumber(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) < data.editCount) { // Process model-save event every 250ms to reduce load - const onModelsSavedWorker = this._register(new RunOnceWorker(e => { - e.forEach(event => { - if (event.kind === StateChange.SAVED) { - const model = modelService.getModel(event.resource); - if (model && model.getModeId() === data.languageId && date !== storageService.get(EDITED_LANGUAGE_DATE_KEY, StorageScope.GLOBAL)) { - const editedCount = storageService.getNumber(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; - storageService.store(EDITED_LANGUAGE_COUNT_KEY, editedCount, StorageScope.GLOBAL); - storageService.store(EDITED_LANGUAGE_DATE_KEY, date, StorageScope.GLOBAL); - } + const onModelsSavedWorker = this._register(new RunOnceWorker(models => { + models.forEach(m => { + const model = modelService.getModel(m.resource); + if (model && model.getModeId() === data.languageId && date !== storageService.get(EDITED_LANGUAGE_DATE_KEY, StorageScope.GLOBAL)) { + const editedCount = storageService.getNumber(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; + storageService.store(EDITED_LANGUAGE_COUNT_KEY, editedCount, StorageScope.GLOBAL); + storageService.store(EDITED_LANGUAGE_DATE_KEY, date, StorageScope.GLOBAL); } }); }, 250)); - this._register(textFileService.models.onModelSaved(e => onModelsSavedWorker.work(e))); + this._register(textFileService.models.onModelSaved(m => onModelsSavedWorker.work(m))); } const lastSessionDate = storageService.get(LAST_SESSION_DATE_KEY, StorageScope.GLOBAL, new Date(0).toDateString()); diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index eca81528ce4..0682ad8239a 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -19,7 +19,7 @@ import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; import { configurationTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { ITextFileService, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { extname, basename, isEqual, isEqualOrParent, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; @@ -124,15 +124,15 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr this._register(configurationTelemetry(telemetryService, configurationService)); // Files Telemetry - this._register(textFileService.models.onModelLoaded(e => this.onTextFileModelLoaded(e))); - this._register(textFileService.models.onModelSaved(e => this.onTextFileModelSaved(e))); + this._register(textFileService.models.onModelLoaded(m => this.onTextFileModelLoaded(m))); + this._register(textFileService.models.onModelSaved(m => this.onTextFileModelSaved(m))); // Lifecycle this._register(lifecycleService.onShutdown(() => this.dispose())); } - private onTextFileModelLoaded(event: TextFileModelChangeEvent): void { - const settingsType = this.getTypeIfSettings(event.resource); + private onTextFileModelLoaded(model: ITextFileEditorModel): void { + const settingsType = this.getTypeIfSettings(model.resource); if (settingsType) { type SettingsReadClassification = { settingsType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; @@ -142,12 +142,12 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr } else { type FileGetClassification = {} & FileTelemetryDataFragment; - this.telemetryService.publicLog2('fileGet', this.getTelemetryData(event.resource)); + this.telemetryService.publicLog2('fileGet', this.getTelemetryData(model.resource)); } } - private onTextFileModelSaved(event: TextFileModelChangeEvent): void { - const settingsType = this.getTypeIfSettings(event.resource); + private onTextFileModelSaved(model: ITextFileEditorModel): void { + const settingsType = this.getTypeIfSettings(model.resource); if (settingsType) { type SettingsWrittenClassification = { settingsType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; @@ -155,7 +155,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr this.telemetryService.publicLog2<{ settingsType: string }, SettingsWrittenClassification>('settingsWritten', { settingsType }); // Do not log write to user settings.json and .vscode folder as a filePUT event as it ruins our JSON usage data } else { type FilePutClassfication = {} & FileTelemetryDataFragment; - this.telemetryService.publicLog2('filePUT', this.getTelemetryData(event.resource)); + this.telemetryService.publicLog2('filePUT', this.getTelemetryData(model.resource)); } } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 0db12b9d2fd..95f1ae33223 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -7,7 +7,7 @@ import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { ITextFileEditorModel, ITextFileEditorModelManager, TextFileModelChangeEvent, StateChange, IModelLoadOrCreateOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileEditorModel, ITextFileEditorModelManager, StateChange, IModelLoadOrCreateOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ResourceMap } from 'vs/base/common/map'; @@ -18,25 +18,25 @@ import { onUnexpectedError } from 'vs/base/common/errors'; export class TextFileEditorModelManager extends Disposable implements ITextFileEditorModelManager { - private readonly _onModelLoaded = this._register(new Emitter()); + private readonly _onModelLoaded = this._register(new Emitter()); readonly onModelLoaded = this._onModelLoaded.event; - private readonly _onModelDirty = this._register(new Emitter()); + private readonly _onModelDirty = this._register(new Emitter()); readonly onModelDirty = this._onModelDirty.event; - private readonly _onModelSaveError = this._register(new Emitter()); + private readonly _onModelSaveError = this._register(new Emitter()); readonly onModelSaveError = this._onModelSaveError.event; - private readonly _onModelSaved = this._register(new Emitter()); + private readonly _onModelSaved = this._register(new Emitter()); readonly onModelSaved = this._onModelSaved.event; - private readonly _onModelReverted = this._register(new Emitter()); + private readonly _onModelReverted = this._register(new Emitter()); readonly onModelReverted = this._onModelReverted.event; - private readonly _onModelEncodingChanged = this._register(new Emitter()); + private readonly _onModelEncodingChanged = this._register(new Emitter()); readonly onModelEncodingChanged = this._onModelEncodingChanged.event; - private readonly _onModelOrphanedChanged = this._register(new Emitter()); + private readonly _onModelOrphanedChanged = this._register(new Emitter()); readonly onModelOrphanedChanged = this._onModelOrphanedChanged.event; private readonly mapResourceToDisposeListener = new ResourceMap(); @@ -130,28 +130,27 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // Install state change listener this.mapResourceToStateChangeListener.set(resource, model.onDidChangeState(state => { - const event = new TextFileModelChangeEvent(newModel, state); switch (state) { case StateChange.LOADED: - this._onModelLoaded.fire(event); + this._onModelLoaded.fire(newModel); break; case StateChange.DIRTY: - this._onModelDirty.fire(event); + this._onModelDirty.fire(newModel); break; case StateChange.SAVE_ERROR: - this._onModelSaveError.fire(event); + this._onModelSaveError.fire(newModel); break; case StateChange.SAVED: - this._onModelSaved.fire(event); + this._onModelSaved.fire(newModel); break; case StateChange.REVERTED: - this._onModelReverted.fire(event); + this._onModelReverted.fire(newModel); break; case StateChange.ENCODING: - this._onModelEncodingChanged.fire(event); + this._onModelEncodingChanged.fire(newModel); break; case StateChange.ORPHANED_CHANGE: - this._onModelOrphanedChanged.fire(event); + this._onModelOrphanedChanged.fire(newModel); break; } })); @@ -168,7 +167,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // Model can be dirty if a backup was restored, so we make sure to have this event delivered if (resolvedModel.isDirty()) { - this._onModelDirty.fire(new TextFileModelChangeEvent(resolvedModel, StateChange.DIRTY)); + this._onModelDirty.fire(resolvedModel); } // Remove from pending loads diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 92056c53d84..561aa04380b 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -285,22 +285,6 @@ export const enum StateChange { ORPHANED_CHANGE } -export class TextFileModelChangeEvent { - private _resource: URI; - - constructor(model: ITextFileEditorModel, private _kind: StateChange) { - this._resource = model.resource; - } - - get resource(): URI { - return this._resource; - } - - get kind(): StateChange { - return this._kind; - } -} - export interface ITextFileOperationResult { results: IResult[]; } @@ -378,14 +362,14 @@ export interface IModelLoadOrCreateOptions { export interface ITextFileEditorModelManager { - readonly onModelEncodingChanged: Event; - readonly onModelOrphanedChanged: Event; + readonly onModelEncodingChanged: Event; + readonly onModelOrphanedChanged: Event; - readonly onModelLoaded: Event; - readonly onModelDirty: Event; - readonly onModelSaveError: Event; - readonly onModelSaved: Event; - readonly onModelReverted: Event; + readonly onModelLoaded: Event; + readonly onModelDirty: Event; + readonly onModelSaveError: Event; + readonly onModelSaved: Event; + readonly onModelReverted: Event; get(resource: URI): ITextFileEditorModel | undefined; diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts index da4fbc06caf..4d5ee1fcee7 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -139,32 +139,32 @@ suite('Files - TextFileEditorModelManager', () => { let savedCounter = 0; let encodingCounter = 0; - manager.onModelLoaded(e => { - if (e.resource.toString() === resource1.toString()) { + manager.onModelLoaded(model => { + if (model.resource.toString() === resource1.toString()) { loadedCounter++; } }); - manager.onModelDirty(e => { - if (e.resource.toString() === resource1.toString()) { + manager.onModelDirty(model => { + if (model.resource.toString() === resource1.toString()) { dirtyCounter++; } }); - manager.onModelReverted(e => { - if (e.resource.toString() === resource1.toString()) { + manager.onModelReverted(model => { + if (model.resource.toString() === resource1.toString()) { revertedCounter++; } }); - manager.onModelSaved(e => { - if (e.resource.toString() === resource1.toString()) { + manager.onModelSaved(model => { + if (model.resource.toString() === resource1.toString()) { savedCounter++; } }); - manager.onModelEncodingChanged(e => { - if (e.resource.toString() === resource1.toString()) { + manager.onModelEncodingChanged(model => { + if (model.resource.toString() === resource1.toString()) { encodingCounter++; } }); From b4bed747eb17efe1954dee0e3e1ee449ff15b78d Mon Sep 17 00:00:00 2001 From: Luis Oliveira Date: Mon, 13 Jan 2020 12:24:27 +0000 Subject: [PATCH 188/843] Formatting changes --- src/vs/base/browser/keyboardEvent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/keyboardEvent.ts b/src/vs/base/browser/keyboardEvent.ts index 1f312c6fae1..9303029b89c 100644 --- a/src/vs/base/browser/keyboardEvent.ts +++ b/src/vs/base/browser/keyboardEvent.ts @@ -228,7 +228,7 @@ export class StandardKeyboardEvent implements IKeyboardEvent { let e = source; this.browserEvent = e; - this.target = (e as any).path ? (e as any).path[0] : e.target; + this.target = (e as any).path ? (e as any).path[0] : e.target; this.ctrlKey = e.ctrlKey; this.shiftKey = e.shiftKey; From 4766c9d226da8828b2661089f2908c73ff63c953 Mon Sep 17 00:00:00 2001 From: Luis Oliveira Date: Mon, 13 Jan 2020 12:51:40 +0000 Subject: [PATCH 189/843] Fixing small issue in dom.ts --- src/vs/base/browser/dom.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 5bf15cdfa1b..f45c1697c78 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -820,7 +820,7 @@ export function hasParentWithClass(node: HTMLElement, clazz: string, stopAtClazz return !!findParentWithClass(node, clazz, stopAtClazzOrNode); } -export function createStyleSheet(container: HTMLElement): HTMLStyleElement { +export function createStyleSheet(container: HTMLElement | null): HTMLStyleElement { if (!container) { if ((window as any).monacoShadowRoot) { container = (window as any).monacoShadowRoot.querySelector('head'); @@ -833,7 +833,7 @@ export function createStyleSheet(container: HTMLElement): HTMLStyleElement { let style = document.createElement('style'); style.type = 'text/css'; style.media = 'screen'; - container.appendChild(style); + (container as HTMLElement).appendChild(style); return style; } @@ -846,7 +846,7 @@ export function createMetaElement(container: HTMLElement = document.getElementsB let _sharedStyleSheet: HTMLStyleElement | null = null; function getSharedStyleSheet(): HTMLStyleElement { if (!_sharedStyleSheet) { - _sharedStyleSheet = createStyleSheet(); + _sharedStyleSheet = createStyleSheet(null); } return _sharedStyleSheet; } From 02d39920cd494f55d793c431a6e7af3a91f2d4b5 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Mon, 13 Jan 2020 11:35:27 +0100 Subject: [PATCH 190/843] Better handling of multiple spaces when using the DOM for line breaks --- .../browser/view/domLineBreaksComputer.ts | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts index b22d6757f43..6ec11f3db88 100644 --- a/src/vs/editor/browser/view/domLineBreaksComputer.ts +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -84,15 +84,18 @@ function renderLine(lineIndex: number, lineContent: string, tabSize: number, sb: // sb.appendASCIIString('" dir="ltr'); // } + const len = lineContent.length; let visibleColumn = 0; let charOffset = 0; let charOffsets: number[] = []; let visibleColumns: number[] = []; + let nextCharCode = (0 < len ? lineContent.charCodeAt(0) : CharCode.Null); - for (let charIndex = 0, len = lineContent.length; charIndex < len; charIndex++) { + for (let charIndex = 0; charIndex < len; charIndex++) { charOffsets[charIndex] = charOffset; visibleColumns[charIndex] = visibleColumn; - const charCode = lineContent.charCodeAt(charIndex); + const charCode = nextCharCode; + nextCharCode = (charIndex + 1 < len ? lineContent.charCodeAt(charIndex + 1) : CharCode.Null); let producedCharacters = 1; let charWidth = 1; switch (charCode) { @@ -100,14 +103,20 @@ function renderLine(lineIndex: number, lineContent: string, tabSize: number, sb: producedCharacters = (tabSize - (visibleColumn % tabSize)); charWidth = producedCharacters; for (let space = 1; space <= producedCharacters; space++) { - sb.appendASCII(CharCode.Space); - // sb.write1(0xA0); //   + if (space < producedCharacters) { + sb.write1(0xA0); //   + } else { + sb.appendASCII(CharCode.Space); + } } break; case CharCode.Space: - sb.appendASCII(CharCode.Space); - // sb.write1(0xA0); //   + if (nextCharCode === CharCode.Space) { + sb.write1(0xA0); //   + } else { + sb.appendASCII(CharCode.Space); + } break; case CharCode.LessThan: From c6dec6ef12f74cdb4998beb63fcc0b44d9aa1edb Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Mon, 13 Jan 2020 12:34:48 +0100 Subject: [PATCH 191/843] Add support for WrappingIndent.Same in DOMLineBreaksComputer --- .../browser/view/domLineBreaksComputer.ts | 116 ++++++++++++++---- 1 file changed, 95 insertions(+), 21 deletions(-) diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts index 6ec11f3db88..66d89506b8c 100644 --- a/src/vs/editor/browser/view/domLineBreaksComputer.ts +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -36,26 +36,82 @@ export class DOMLineBreaksComputerFactory implements ILineBreaksComputerFactory requests.push(lineText); }, finalize: () => { - return createLineBreaks(this._fontInfo, tabSize, wrappingColumn, wrappingIndent, requests); + return createLineBreaks(requests, this._fontInfo, tabSize, wrappingColumn, wrappingIndent); } }; } } -function createLineBreaks(fontInfo: FontInfo, tabSize: number, firstLineBreakColumn: number, wrappingIndent: WrappingIndent, requests: string[]): (LineBreakData | null)[] { - const width = Math.round(firstLineBreakColumn * fontInfo.typicalHalfwidthCharacterWidth); +function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: number, firstLineBreakColumn: number, wrappingIndent: WrappingIndent): (LineBreakData | null)[] { + if (firstLineBreakColumn === -1) { + const result: null[] = []; + for (let i = 0, len = requests.length; i < len; i++) { + result[i] = null; + } + return result; + } + + const overallWidth = Math.round(firstLineBreakColumn * fontInfo.typicalHalfwidthCharacterWidth); + + // Cannot respect WrappingIndent.Indent and WrappingIndent.DeepIndent because that would require + // two dom layouts, in order to first set the width of the first line, and then set the width of the wrapped lines + if (wrappingIndent === WrappingIndent.Indent || wrappingIndent === WrappingIndent.DeepIndent) { + wrappingIndent = WrappingIndent.Same; + } const containerDomNode = document.createElement('div'); Configuration.applyFontInfoSlow(containerDomNode, fontInfo); - containerDomNode.style.width = `${width}px`; const sb = createStringBuilder(10000); - const charOffsets: number[][] = []; - const visibleColumns: number[][] = []; + const firstNonWhitespaceIndices: number[] = []; + const wrappedTextIndentLengths: number[] = []; + const renderLineContents: string[] = []; + const allCharOffsets: number[][] = []; + const allVisibleColumns: number[][] = []; for (let i = 0; i < requests.length; i++) { - const r = renderLine(i, requests[i], tabSize, sb); - charOffsets[i] = r[0]; - visibleColumns[i] = r[1]; + const lineContent = requests[i]; + + let firstNonWhitespaceIndex = 0; + let wrappedTextIndentLength = 0; + let width = overallWidth; + + if (wrappingIndent !== WrappingIndent.None) { + firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent); + if (firstNonWhitespaceIndex === -1) { + // all whitespace line + firstNonWhitespaceIndex = 0; + + } else { + // Track existing indent + + for (let i = 0; i < firstNonWhitespaceIndex; i++) { + const charWidth = ( + lineContent.charCodeAt(i) === CharCode.Tab + ? (tabSize - (wrappedTextIndentLength % tabSize)) + : 1 + ); + wrappedTextIndentLength += charWidth; + } + + const indentWidth = Math.ceil(fontInfo.spaceWidth * wrappedTextIndentLength); + + // Force sticking to beginning of line if no character would fit except for the indentation + if (indentWidth + fontInfo.typicalFullwidthCharacterWidth > overallWidth) { + firstNonWhitespaceIndex = 0; + wrappedTextIndentLength = 0; + } else { + width = overallWidth - indentWidth; + } + } + } + + const renderLineContent = lineContent.substr(firstNonWhitespaceIndex); + const tmp = renderLine(renderLineContent, wrappedTextIndentLength, tabSize, width, sb); + firstNonWhitespaceIndices[i] = firstNonWhitespaceIndex; + wrappedTextIndentLengths[i] = wrappedTextIndentLength; + renderLineContents[i] = renderLineContent; + allCharOffsets[i] = tmp[0]; + allVisibleColumns[i] = tmp[1]; } containerDomNode.innerHTML = sb.build(); @@ -71,21 +127,45 @@ function createLineBreaks(fontInfo: FontInfo, tabSize: number, firstLineBreakCol let result: (LineBreakData | null)[] = []; for (let i = 0; i < requests.length; i++) { const lineDomNode = lineDomNodes[i]; - result[i] = readLineBreaks(range, lineDomNode, requests[i], charOffsets[i], visibleColumns[i]); + const breakOffsets: number[] | null = readLineBreaks(range, lineDomNode, renderLineContents[i], allCharOffsets[i]); + if (breakOffsets === null) { + result[i] = null; + continue; + } + + const firstNonWhitespaceIndex = firstNonWhitespaceIndices[i]; + const wrappedTextIndentLength = wrappedTextIndentLengths[i]; + const visibleColumns = allVisibleColumns[i]; + + const breakOffsetsVisibleColumn: number[] = []; + for (let j = 0, len = breakOffsets.length; j < len; j++) { + breakOffsetsVisibleColumn[j] = visibleColumns[breakOffsets[j]]; + } + + if (firstNonWhitespaceIndex !== 0) { + // All break offsets are relative to the renderLineContent, make them absolute again + for (let j = 0, len = breakOffsets.length; j < len; j++) { + breakOffsets[j] += firstNonWhitespaceIndex; + } + } + + result[i] = new LineBreakData(breakOffsets, breakOffsetsVisibleColumn, wrappedTextIndentLength); } document.body.removeChild(containerDomNode); return result; } -function renderLine(lineIndex: number, lineContent: string, tabSize: number, sb: IStringBuilder): [number[], number[]] { - sb.appendASCIIString('
'); +function renderLine(lineContent: string, initialVisibleColumn: number, tabSize: number, width: number, sb: IStringBuilder): [number[], number[]] { + sb.appendASCIIString('
'); // if (containsRTL) { // sb.appendASCIIString('" dir="ltr'); // } const len = lineContent.length; - let visibleColumn = 0; + let visibleColumn = initialVisibleColumn; let charOffset = 0; let charOffsets: number[] = []; let visibleColumns: number[] = []; @@ -163,7 +243,7 @@ function renderLine(lineIndex: number, lineContent: string, tabSize: number, sb: return [charOffsets, visibleColumns]; } -function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent: string, charOffsets: number[], visibleColumns: number[]): LineBreakData | null { +function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent: string, charOffsets: number[]): number[] | null { if (lineContent.length <= 1) { return null; } @@ -177,13 +257,7 @@ function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent: } breakOffsets.push(lineContent.length); - - const breakOffsetsVisibleColumn = []; - for (let i = 0, len = breakOffsets.length; i < len; i++) { - breakOffsetsVisibleColumn[i] = visibleColumns[breakOffsets[i]]; - } - - return new LineBreakData(breakOffsets, breakOffsetsVisibleColumn, 0); + return breakOffsets; } type MaybeRects = ClientRectList | DOMRectList | null; From 314d0e12a90c1c98cece1528da6f94865f1490cc Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Mon, 13 Jan 2020 12:58:40 +0100 Subject: [PATCH 192/843] Introduce editor.wrappingAlgorithm --- .../browser/view/domLineBreaksComputer.ts | 4 +-- .../editor/browser/widget/codeEditorWidget.ts | 8 ++--- src/vs/editor/common/config/editorOptions.ts | 17 +++++++++++ .../common/viewModel/splitLinesCollection.ts | 29 +++++++++++++++---- .../editor/common/viewModel/viewModelImpl.ts | 6 ++-- .../test/browser/commands/sideEditing.test.ts | 3 +- .../test/browser/controller/cursor.test.ts | 21 +++++++++----- .../controller/cursorMoveCommand.test.ts | 3 +- .../viewModel/splitLinesCollection.test.ts | 15 ++++------ .../test/common/viewModel/testViewModel.ts | 9 +++--- src/vs/monaco.d.ts | 5 ++++ 11 files changed, 78 insertions(+), 42 deletions(-) diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts index 66d89506b8c..11db5d68493 100644 --- a/src/vs/editor/browser/view/domLineBreaksComputer.ts +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -116,9 +116,7 @@ function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: numbe containerDomNode.innerHTML = sb.build(); containerDomNode.style.position = 'absolute'; - containerDomNode.style.right = '0'; - containerDomNode.style.bottom = '0'; - containerDomNode.style.zIndex = '10000'; + containerDomNode.style.top = '10000'; document.body.appendChild(containerDomNode); let range = document.createRange(); diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index d9ac4a31b0d..6d584cb146c 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -54,7 +54,6 @@ import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/m import { DOMLineBreaksComputerFactory } from 'vs/editor/browser/view/domLineBreaksComputer'; let EDITOR_ID = 0; -const useDOMLineBreaksComputerFactory = false; export interface ICodeEditorWidgetOptions { /** @@ -1343,11 +1342,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._id, this._configuration, model, - ( - useDOMLineBreaksComputerFactory - ? DOMLineBreaksComputerFactory.create(this._configuration.options) - : MonospaceLineBreaksComputerFactory.create(this._configuration.options) - ), + DOMLineBreaksComputerFactory.create(this._configuration.options), + MonospaceLineBreaksComputerFactory.create(this._configuration.options), (callback) => dom.scheduleAtNextAnimationFrame(callback) ); diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 00308b411ea..056e4ba7a0d 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -262,6 +262,11 @@ export interface IEditorOptions { * Defaults to 'same' in vscode and to 'none' in monaco-editor. */ wrappingIndent?: 'none' | 'same' | 'indent' | 'deepIndent'; + /** + * Controls the wrapping algorithm to use. + * Defaults to 'monospace'. + */ + wrappingAlgorithm?: 'monospace' | 'dom'; /** * Configure word wrapping characters. A break will be introduced before these characters. * Defaults to '([{‘“〈《「『【〔([{「£¥$£¥++'. @@ -3158,6 +3163,7 @@ export const enum EditorOption { wordWrapColumn, wordWrapMinified, wrappingIndent, + wrappingAlgorithm, // Leave these at the end (because they have dependencies!) editorClassName, @@ -3722,6 +3728,17 @@ export const EditorOptions = { description: nls.localize('wrappingIndent', "Controls the indentation of wrapped lines."), } )), + wrappingAlgorithm: register(new EditorStringEnumOption( + EditorOption.wrappingAlgorithm, 'wrappingAlgorithm', + 'monospace', ['monospace', 'dom'], + { + enumDescriptions: [ + nls.localize('wrappingAlgorithm.monospace', "Assumes that all characters are of the same width. This is a fast algorithm."), + nls.localize('wrappingAlgorithm.dom', "Delegates wrapping points computation to the DOM. This is a slow algorithm, that might cause freezes for large files.") + ], + description: nls.localize('wrappingAlgorithm', "Controls the algorithm that computes wrapping points.") + } + )), // Leave these at the end (because they have dependencies!) editorClassName: register(new EditorClassName()), diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 726444f3b11..37a9e6a02c6 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -264,11 +264,16 @@ class LineNumberMapper { } } +const usDOMLineBreaksComputerFactory = false; + export class SplitLinesCollection implements IViewModelLinesCollection { private readonly model: ITextModel; private _validModelVersionId: number; + private readonly _domLineBreaksComputerFactory: ILineBreaksComputerFactory; + private readonly _monospaceLineBreaksComputerFactory: ILineBreaksComputerFactory; + private wrappingColumn: number; private columnsForFullWidthChar: number; private wrappingIndent: WrappingIndent; @@ -277,18 +282,25 @@ export class SplitLinesCollection implements IViewModelLinesCollection { private prefixSumComputer!: LineNumberMapper; - private readonly linePositionMapperFactory: ILineBreaksComputerFactory; - private hiddenAreasIds!: string[]; - constructor(model: ITextModel, linePositionMapperFactory: ILineBreaksComputerFactory, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent) { + constructor( + model: ITextModel, + domLineBreaksComputerFactory: ILineBreaksComputerFactory, + monospaceLineBreaksComputerFactory: ILineBreaksComputerFactory, + tabSize: number, + wrappingColumn: number, + columnsForFullWidthChar: number, + wrappingIndent: WrappingIndent + ) { this.model = model; this._validModelVersionId = -1; + this._domLineBreaksComputerFactory = domLineBreaksComputerFactory; + this._monospaceLineBreaksComputerFactory = monospaceLineBreaksComputerFactory; this.tabSize = tabSize; this.wrappingColumn = wrappingColumn; this.columnsForFullWidthChar = columnsForFullWidthChar; this.wrappingIndent = wrappingIndent; - this.linePositionMapperFactory = linePositionMapperFactory; this._constructLines(/*resetHiddenAreas*/true, null); } @@ -310,7 +322,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { let linesContent = this.model.getLinesContent(); const lineCount = linesContent.length; - const lineBreaksComputer = this.linePositionMapperFactory.createLineBreaksComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent); + const lineBreaksComputer = this.createLineBreaksComputer(); for (let i = 0; i < lineCount; i++) { lineBreaksComputer.addRequest(linesContent[i], previousLineBreaks ? previousLineBreaks[i] : null); } @@ -495,7 +507,12 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public createLineBreaksComputer(): ILineBreaksComputer { - return this.linePositionMapperFactory.createLineBreaksComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent); + const lineBreaksComputerFactory = ( + usDOMLineBreaksComputerFactory + ? this._domLineBreaksComputerFactory + : this._monospaceLineBreaksComputerFactory + ); + return lineBreaksComputerFactory.createLineBreaksComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent); } public onModelFlushed(): void { diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 718c9fc090e..2e9c57986dd 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -46,7 +46,8 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel editorId: number, configuration: editorCommon.IConfiguration, model: ITextModel, - lineMapperFactory: ILineBreaksComputerFactory, + domLineBreaksComputerFactory: ILineBreaksComputerFactory, + monospaceLineBreaksComputerFactory: ILineBreaksComputerFactory, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable ) { super(); @@ -72,7 +73,8 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel this.lines = new SplitLinesCollection( this.model, - lineMapperFactory, + domLineBreaksComputerFactory, + monospaceLineBreaksComputerFactory, this.model.getOptions().tabSize, wrappingInfo.wrappingColumn, fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth, diff --git a/src/vs/editor/test/browser/commands/sideEditing.test.ts b/src/vs/editor/test/browser/commands/sideEditing.test.ts index 67ae4e4d174..540771d0c72 100644 --- a/src/vs/editor/test/browser/commands/sideEditing.test.ts +++ b/src/vs/editor/test/browser/commands/sideEditing.test.ts @@ -201,7 +201,8 @@ suite('SideEditing', () => { function _runTest(selection: Selection, editRange: Range, editText: string, editForceMoveMarkers: boolean, expected: Selection, msg: string): void { const model = TextModel.createFromString(LINES.join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); + const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(config.options); + const viewModel = new ViewModel(0, config, model, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!); const cursor = new Cursor(config, model, viewModel); cursor.setSelections('tests', [selection]); diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 7d82c86f55e..7b9cafb8c95 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -12,7 +12,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; -import { Handler, ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; +import { Handler, ICommand, ICursorStateComputerData, IEditOperationBuilder, IConfiguration } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference, EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { IState, ITokenizationSupport, LanguageIdentifier, TokenizationRegistry } from 'vs/editor/common/modes'; @@ -131,6 +131,11 @@ function assertCursor(cursor: Cursor, what: Position | Selection | Selection[]): assert.deepEqual(actual, expected); } +function createViewModel(configuration: IConfiguration, model: ITextModel): ViewModel { + const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(configuration.options); + return new ViewModel(0, configuration, model, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!); +} + suite('Editor Controller - Cursor', () => { const LINE1 = ' \tMy First Line\t '; const LINE2 = '\tMy Second Line'; @@ -153,7 +158,7 @@ suite('Editor Controller - Cursor', () => { thisModel = createTextModel(text); thisConfiguration = new TestConfiguration({}); - thisViewModel = new ViewModel(0, thisConfiguration, thisModel, MonospaceLineBreaksComputerFactory.create(thisConfiguration.options), null!); + thisViewModel = createViewModel(thisConfiguration, thisModel); thisCursor = new Cursor(thisConfiguration, thisModel, thisViewModel); }); @@ -777,7 +782,7 @@ suite('Editor Controller - Cursor', () => { 'var newer = require("gulp-newer");', ].join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); + const viewModel = createViewModel(config, model); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 1, 4, false); @@ -817,7 +822,7 @@ suite('Editor Controller - Cursor', () => { '', ].join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); + const viewModel = createViewModel(config, model); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 10, 10, false); @@ -881,7 +886,7 @@ suite('Editor Controller - Cursor', () => { '', ].join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); + const viewModel = createViewModel(config, model); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 10, 10, false); @@ -930,7 +935,7 @@ suite('Editor Controller - Cursor', () => { 'var newer = require("gulp-newer");', ].join('\n')); const config = new TestConfiguration({}); - const viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); + const viewModel = createViewModel(config, model); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 1, 4, false); @@ -2075,7 +2080,7 @@ suite('Editor Controller - Regression tests', () => { wordWrap: 'wordWrapColumn', wordWrapColumn: 100 }); - const viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); + const viewModel = createViewModel(config, model); const cursor = new Cursor(config, model, viewModel); moveTo(cursor, 1, 43, false); @@ -3835,7 +3840,7 @@ function usingCursor(opts: ICursorOpts, callback: (model: TextModel, cursor: Cur let model = createTextModel(opts.text.join('\n'), opts.modelOpts, opts.languageIdentifier); model.forceTokenization(model.getLineCount()); let config = new TestConfiguration(opts.editorOpts || {}); - let viewModel = new ViewModel(0, config, model, MonospaceLineBreaksComputerFactory.create(config.options), null!); + let viewModel = createViewModel(config, model); let cursor = new Cursor(config, model, viewModel); callback(model, cursor); diff --git a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts index 117cf54372d..36f2f54dc23 100644 --- a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts +++ b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts @@ -33,7 +33,8 @@ suite('Cursor move command test', () => { thisModel = TextModel.createFromString(text); thisConfiguration = new TestConfiguration({}); - thisViewModel = new ViewModel(0, thisConfiguration, thisModel, MonospaceLineBreaksComputerFactory.create(thisConfiguration.options), null!); + const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(thisConfiguration.options); + thisViewModel = new ViewModel(0, thisConfiguration, thisModel, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!); thisCursor = new Cursor(thisConfiguration, thisModel, thisViewModel); }); diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index 7209c90e881..0f6be3855ad 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -19,7 +19,6 @@ import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; - suite('Editor ViewModel - SplitLinesCollection', () => { test('SplitLine', () => { let model1 = createModel('My First LineMy Second LineAnd another one'); @@ -95,10 +94,7 @@ suite('Editor ViewModel - SplitLinesCollection', () => { const wordWrapBreakBeforeCharacters = config.options.get(EditorOption.wordWrapBreakBeforeCharacters); const wrappingIndent = config.options.get(EditorOption.wrappingIndent); - const lineBreaksComputerFactory = new MonospaceLineBreaksComputerFactory( - wordWrapBreakBeforeCharacters, - wordWrapBreakAfterCharacters - ); + const lineBreaksComputerFactory = new MonospaceLineBreaksComputerFactory(wordWrapBreakBeforeCharacters, wordWrapBreakAfterCharacters); const model = TextModel.createFromString([ 'int main() {', @@ -112,6 +108,7 @@ suite('Editor ViewModel - SplitLinesCollection', () => { const linesCollection = new SplitLinesCollection( model, lineBreaksComputerFactory, + lineBreaksComputerFactory, model.getOptions().tabSize, wrappingInfo.wrappingColumn, fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth, @@ -747,14 +744,12 @@ suite('SplitLinesCollection', () => { const wordWrapBreakBeforeCharacters = configuration.options.get(EditorOption.wordWrapBreakBeforeCharacters); const wrappingIndent = configuration.options.get(EditorOption.wrappingIndent); - const factory = new MonospaceLineBreaksComputerFactory( - wordWrapBreakBeforeCharacters, - wordWrapBreakAfterCharacters - ); + const lineBreaksComputerFactory = new MonospaceLineBreaksComputerFactory(wordWrapBreakBeforeCharacters, wordWrapBreakAfterCharacters); const linesCollection = new SplitLinesCollection( model, - factory, + lineBreaksComputerFactory, + lineBreaksComputerFactory, model.getOptions().tabSize, wrappingInfo.wrappingColumn, fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth, diff --git a/src/vs/editor/test/common/viewModel/testViewModel.ts b/src/vs/editor/test/common/viewModel/testViewModel.ts index fffa9f05aa9..b316d88b004 100644 --- a/src/vs/editor/test/common/viewModel/testViewModel.ts +++ b/src/vs/editor/test/common/viewModel/testViewModel.ts @@ -12,11 +12,10 @@ import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/m export function testViewModel(text: string[], options: IEditorOptions, callback: (viewModel: ViewModel, model: TextModel) => void): void { const EDITOR_ID = 1; - let configuration = new TestConfiguration(options); - - let model = TextModel.createFromString(text.join('\n')); - - let viewModel = new ViewModel(EDITOR_ID, configuration, model, MonospaceLineBreaksComputerFactory.create(configuration.options), null!); + const configuration = new TestConfiguration(options); + const model = TextModel.createFromString(text.join('\n')); + const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(configuration.options); + const viewModel = new ViewModel(EDITOR_ID, configuration, model, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!); callback(viewModel, model); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 5e2f5a15642..95d91f163b9 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2707,6 +2707,11 @@ declare namespace monaco.editor { * Defaults to 'same' in vscode and to 'none' in monaco-editor. */ wrappingIndent?: 'none' | 'same' | 'indent' | 'deepIndent'; + /** + * Controls the wrapping algorithm to use. + * Defaults to 'monospace'. + */ + wrappingAlgorithm?: 'monospace' | 'dom'; /** * Configure word wrapping characters. A break will be introduced before these characters. * Defaults to '([{‘“〈《「『【〔([{「£¥$£¥++'. From 53125e31f0953a83b1e8da711a0f9fbab2fd4bfc Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Mon, 13 Jan 2020 14:51:04 +0100 Subject: [PATCH 193/843] Pass in entire fontInfo when computing line breaks --- .../browser/view/domLineBreaksComputer.ts | 18 ++++------- .../editor/browser/widget/codeEditorWidget.ts | 2 +- .../viewModel/monospaceLineBreaksComputer.ts | 5 +-- .../common/viewModel/splitLinesCollection.ts | 32 +++++++++++-------- .../editor/common/viewModel/viewModelImpl.ts | 4 +-- .../monospaceLineBreaksComputer.test.ts | 18 ++++++++++- .../viewModel/splitLinesCollection.test.ts | 4 +-- 7 files changed, 49 insertions(+), 34 deletions(-) diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts index 11db5d68493..ab567c36c9f 100644 --- a/src/vs/editor/browser/view/domLineBreaksComputer.ts +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ILineBreaksComputerFactory, LineBreakData, ILineBreaksComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; -import { IComputedEditorOptions, EditorOption, WrappingIndent } from 'vs/editor/common/config/editorOptions'; +import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { createStringBuilder, IStringBuilder } from 'vs/editor/common/core/stringBuilder'; import { CharCode } from 'vs/base/common/charCode'; @@ -13,22 +13,16 @@ import { Configuration } from 'vs/editor/browser/config/configuration'; export class DOMLineBreaksComputerFactory implements ILineBreaksComputerFactory { - public static create(options: IComputedEditorOptions): DOMLineBreaksComputerFactory { - return new DOMLineBreaksComputerFactory( - options.get(EditorOption.fontInfo) - ); + public static create(): DOMLineBreaksComputerFactory { + return new DOMLineBreaksComputerFactory(); } - private _fontInfo: FontInfo; - - constructor(fontInfo: FontInfo) { - this._fontInfo = fontInfo; + constructor() { } - public createLineBreaksComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineBreaksComputer { + public createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer { tabSize = tabSize | 0; //@perf wrappingColumn = +wrappingColumn; //@perf - columnsForFullWidthChar = +columnsForFullWidthChar; //@perf let requests: string[] = []; return { @@ -36,7 +30,7 @@ export class DOMLineBreaksComputerFactory implements ILineBreaksComputerFactory requests.push(lineText); }, finalize: () => { - return createLineBreaks(requests, this._fontInfo, tabSize, wrappingColumn, wrappingIndent); + return createLineBreaks(requests, fontInfo, tabSize, wrappingColumn, wrappingIndent); } }; } diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 6d584cb146c..2f8f5cf4f98 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1342,7 +1342,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._id, this._configuration, model, - DOMLineBreaksComputerFactory.create(this._configuration.options), + DOMLineBreaksComputerFactory.create(), MonospaceLineBreaksComputerFactory.create(this._configuration.options), (callback) => dom.scheduleAtNextAnimationFrame(callback) ); diff --git a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts index 546464c2636..97a3c4a7655 100644 --- a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts +++ b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts @@ -8,6 +8,7 @@ import * as strings from 'vs/base/common/strings'; import { WrappingIndent, IComputedEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; import { ILineBreaksComputerFactory, LineBreakData, ILineBreaksComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { FontInfo } from 'vs/editor/common/config/fontInfo'; const enum CharacterClass { NONE = 0, @@ -69,10 +70,9 @@ export class MonospaceLineBreaksComputerFactory implements ILineBreaksComputerFa this.classifier = new WrappingCharacterClassifier(breakBeforeChars, breakAfterChars); } - public createLineBreaksComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineBreaksComputer { + public createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer { tabSize = tabSize | 0; //@perf wrappingColumn = +wrappingColumn; //@perf - columnsForFullWidthChar = +columnsForFullWidthChar; //@perf let requests: string[] = []; let previousBreakingData: (LineBreakData | null)[] = []; @@ -82,6 +82,7 @@ export class MonospaceLineBreaksComputerFactory implements ILineBreaksComputerFa previousBreakingData.push(previousLineBreakData); }, finalize: () => { + const columnsForFullWidthChar = fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth; //@perf let result: (LineBreakData | null)[] = []; for (let i = 0, len = requests.length; i < len; i++) { const previousLineBreakData = previousBreakingData[i]; diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 37a9e6a02c6..bee048ac90a 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -15,6 +15,7 @@ import { PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComp import { ICoordinatesConverter, IOverviewRulerDecorations, ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { ITheme } from 'vs/platform/theme/common/themeService'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { FontInfo } from 'vs/editor/common/config/fontInfo'; export class OutputPosition { outputLineIndex: number; @@ -75,7 +76,7 @@ export interface ILineBreaksComputer { } export interface ILineBreaksComputerFactory { - createLineBreaksComputer(tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineBreaksComputer; + createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer; } export interface ISimpleModel { @@ -108,7 +109,7 @@ export interface ISplitLine { export interface IViewModelLinesCollection extends IDisposable { createCoordinatesConverter(): ICoordinatesConverter; - setWrappingSettings(wrappingIndent: WrappingIndent, wrappingColumn: number, columnsForFullWidthChar: number): boolean; + setWrappingSettings(fontInfo: FontInfo, wrappingColumn: number, wrappingIndent: WrappingIndent): boolean; setTabSize(newTabSize: number): boolean; getHiddenAreas(): Range[]; setHiddenAreas(_ranges: Range[]): boolean; @@ -274,10 +275,10 @@ export class SplitLinesCollection implements IViewModelLinesCollection { private readonly _domLineBreaksComputerFactory: ILineBreaksComputerFactory; private readonly _monospaceLineBreaksComputerFactory: ILineBreaksComputerFactory; - private wrappingColumn: number; - private columnsForFullWidthChar: number; - private wrappingIndent: WrappingIndent; + private fontInfo: FontInfo; private tabSize: number; + private wrappingColumn: number; + private wrappingIndent: WrappingIndent; private lines!: ISplitLine[]; private prefixSumComputer!: LineNumberMapper; @@ -288,18 +289,18 @@ export class SplitLinesCollection implements IViewModelLinesCollection { model: ITextModel, domLineBreaksComputerFactory: ILineBreaksComputerFactory, monospaceLineBreaksComputerFactory: ILineBreaksComputerFactory, + fontInfo: FontInfo, tabSize: number, wrappingColumn: number, - columnsForFullWidthChar: number, wrappingIndent: WrappingIndent ) { this.model = model; this._validModelVersionId = -1; this._domLineBreaksComputerFactory = domLineBreaksComputerFactory; this._monospaceLineBreaksComputerFactory = monospaceLineBreaksComputerFactory; + this.fontInfo = fontInfo; this.tabSize = tabSize; this.wrappingColumn = wrappingColumn; - this.columnsForFullWidthChar = columnsForFullWidthChar; this.wrappingIndent = wrappingIndent; this._constructLines(/*resetHiddenAreas*/true, null); @@ -482,16 +483,19 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return true; } - public setWrappingSettings(wrappingIndent: WrappingIndent, wrappingColumn: number, columnsForFullWidthChar: number): boolean { - if (this.wrappingIndent === wrappingIndent && this.wrappingColumn === wrappingColumn && this.columnsForFullWidthChar === columnsForFullWidthChar) { + public setWrappingSettings(fontInfo: FontInfo, wrappingColumn: number, wrappingIndent: WrappingIndent): boolean { + const equalFontInfo = this.fontInfo.equals(fontInfo); + const equalWrappingColumn = (this.wrappingColumn === wrappingColumn); + const equalWrappingIndent = (this.wrappingIndent === wrappingIndent); + if (equalFontInfo && equalWrappingColumn && equalWrappingIndent) { return false; } - const onlyWrappingColumnChanged = (this.wrappingIndent === wrappingIndent && this.wrappingColumn !== wrappingColumn && this.columnsForFullWidthChar === columnsForFullWidthChar); + const onlyWrappingColumnChanged = (equalFontInfo && !equalWrappingColumn && equalWrappingIndent); - this.wrappingIndent = wrappingIndent; + this.fontInfo = fontInfo; this.wrappingColumn = wrappingColumn; - this.columnsForFullWidthChar = columnsForFullWidthChar; + this.wrappingIndent = wrappingIndent; let previousLineBreaks: ((LineBreakData | null)[]) | null = null; if (onlyWrappingColumnChanged) { @@ -512,7 +516,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { ? this._domLineBreaksComputerFactory : this._monospaceLineBreaksComputerFactory ); - return lineBreaksComputerFactory.createLineBreaksComputer(this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent); + return lineBreaksComputerFactory.createLineBreaksComputer(this.fontInfo, this.tabSize, this.wrappingColumn, this.wrappingIndent); } public onModelFlushed(): void { @@ -1453,7 +1457,7 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return false; } - public setWrappingSettings(_wrappingIndent: WrappingIndent, _wrappingColumn: number, _columnsForFullWidthChar: number): boolean { + public setWrappingSettings(_fontInfo: FontInfo, _wrappingColumn: number, _wrappingIndent: WrappingIndent): boolean { return false; } diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 2e9c57986dd..f6b5d5eeb4f 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -75,9 +75,9 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel this.model, domLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, + fontInfo, this.model.getOptions().tabSize, wrappingInfo.wrappingColumn, - fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth, wrappingIndent ); } @@ -157,7 +157,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const fontInfo = options.get(EditorOption.fontInfo); const wrappingIndent = options.get(EditorOption.wrappingIndent); - if (this.lines.setWrappingSettings(wrappingIndent, wrappingInfo.wrappingColumn, fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth)) { + if (this.lines.setWrappingSettings(fontInfo, wrappingInfo.wrappingColumn, wrappingIndent)) { eventsCollector.emit(new viewEvents.ViewFlushedEvent()); eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); diff --git a/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts b/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts index a95095bbc99..b0bacb0768b 100644 --- a/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts +++ b/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { WrappingIndent, EditorOptions } from 'vs/editor/common/config/editorOptions'; import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; import { ILineBreaksComputerFactory, LineBreakData } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { FontInfo } from 'vs/editor/common/config/fontInfo'; function parseAnnotatedText(annotatedText: string): { text: string; indices: number[]; } { let text = ''; @@ -43,7 +44,22 @@ function toAnnotatedText(text: string, lineBreakData: LineBreakData | null): str } function getLineBreakData(factory: ILineBreaksComputerFactory, tabSize: number, breakAfter: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, text: string, previousLineBreakData: LineBreakData | null): LineBreakData | null { - const lineBreaksComputer = factory.createLineBreaksComputer(tabSize, breakAfter, columnsForFullWidthChar, wrappingIndent); + const fontInfo = new FontInfo({ + zoomLevel: 0, + fontFamily: 'testFontFamily', + fontWeight: 'normal', + fontSize: 14, + fontFeatureSettings: '', + lineHeight: 19, + letterSpacing: 0, + isMonospace: true, + typicalHalfwidthCharacterWidth: 7, + typicalFullwidthCharacterWidth: 14, + canUseHalfwidthRightwardsArrow: true, + spaceWidth: 7, + maxDigitWidth: 7 + }, false); + const lineBreaksComputer = factory.createLineBreaksComputer(fontInfo, tabSize, breakAfter, wrappingIndent); const previousLineBreakDataClone = previousLineBreakData ? new LineBreakData(previousLineBreakData.breakOffsets.slice(0), previousLineBreakData.breakOffsetsVisibleColumn.slice(0), previousLineBreakData.wrappedTextIndentLength) : null; lineBreaksComputer.addRequest(text, previousLineBreakDataClone); return lineBreaksComputer.finalize()[0]; diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index 0f6be3855ad..53234a03a3a 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -109,9 +109,9 @@ suite('Editor ViewModel - SplitLinesCollection', () => { model, lineBreaksComputerFactory, lineBreaksComputerFactory, + fontInfo, model.getOptions().tabSize, wrappingInfo.wrappingColumn, - fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth, wrappingIndent ); @@ -750,9 +750,9 @@ suite('SplitLinesCollection', () => { model, lineBreaksComputerFactory, lineBreaksComputerFactory, + fontInfo, model.getOptions().tabSize, wrappingInfo.wrappingColumn, - fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth, wrappingIndent ); From 519ceb6283a884a82dcabc536ecb8a9bc125adbc Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Mon, 13 Jan 2020 15:00:08 +0100 Subject: [PATCH 194/843] Respect editor.wrappingAlgorithm --- src/vs/editor/common/config/editorOptions.ts | 3 ++- .../common/viewModel/splitLinesCollection.ts | 21 +++++++++++-------- .../editor/common/viewModel/viewModelImpl.ts | 9 +++++--- .../viewModel/splitLinesCollection.test.ts | 2 ++ 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 056e4ba7a0d..2bcf40b9399 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -3730,7 +3730,8 @@ export const EditorOptions = { )), wrappingAlgorithm: register(new EditorStringEnumOption( EditorOption.wrappingAlgorithm, 'wrappingAlgorithm', - 'monospace', ['monospace', 'dom'], + 'monospace' as 'monospace' | 'dom', + ['monospace', 'dom'] as const, { enumDescriptions: [ nls.localize('wrappingAlgorithm.monospace', "Assumes that all characters are of the same width. This is a fast algorithm."), diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index bee048ac90a..dde0d12c37a 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -109,7 +109,7 @@ export interface ISplitLine { export interface IViewModelLinesCollection extends IDisposable { createCoordinatesConverter(): ICoordinatesConverter; - setWrappingSettings(fontInfo: FontInfo, wrappingColumn: number, wrappingIndent: WrappingIndent): boolean; + setWrappingSettings(fontInfo: FontInfo, wrappingAlgorithm: 'monospace' | 'dom', wrappingColumn: number, wrappingIndent: WrappingIndent): boolean; setTabSize(newTabSize: number): boolean; getHiddenAreas(): Range[]; setHiddenAreas(_ranges: Range[]): boolean; @@ -265,8 +265,6 @@ class LineNumberMapper { } } -const usDOMLineBreaksComputerFactory = false; - export class SplitLinesCollection implements IViewModelLinesCollection { private readonly model: ITextModel; @@ -279,6 +277,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { private tabSize: number; private wrappingColumn: number; private wrappingIndent: WrappingIndent; + private wrappingAlgorithm: 'monospace' | 'dom'; private lines!: ISplitLine[]; private prefixSumComputer!: LineNumberMapper; @@ -291,8 +290,9 @@ export class SplitLinesCollection implements IViewModelLinesCollection { monospaceLineBreaksComputerFactory: ILineBreaksComputerFactory, fontInfo: FontInfo, tabSize: number, + wrappingAlgorithm: 'monospace' | 'dom', wrappingColumn: number, - wrappingIndent: WrappingIndent + wrappingIndent: WrappingIndent, ) { this.model = model; this._validModelVersionId = -1; @@ -300,6 +300,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { this._monospaceLineBreaksComputerFactory = monospaceLineBreaksComputerFactory; this.fontInfo = fontInfo; this.tabSize = tabSize; + this.wrappingAlgorithm = wrappingAlgorithm; this.wrappingColumn = wrappingColumn; this.wrappingIndent = wrappingIndent; @@ -483,17 +484,19 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return true; } - public setWrappingSettings(fontInfo: FontInfo, wrappingColumn: number, wrappingIndent: WrappingIndent): boolean { + public setWrappingSettings(fontInfo: FontInfo, wrappingAlgorithm: 'monospace' | 'dom', wrappingColumn: number, wrappingIndent: WrappingIndent): boolean { const equalFontInfo = this.fontInfo.equals(fontInfo); + const equalWrappingAlgorithm = (this.wrappingAlgorithm === wrappingAlgorithm); const equalWrappingColumn = (this.wrappingColumn === wrappingColumn); const equalWrappingIndent = (this.wrappingIndent === wrappingIndent); - if (equalFontInfo && equalWrappingColumn && equalWrappingIndent) { + if (equalFontInfo && equalWrappingAlgorithm && equalWrappingColumn && equalWrappingIndent) { return false; } - const onlyWrappingColumnChanged = (equalFontInfo && !equalWrappingColumn && equalWrappingIndent); + const onlyWrappingColumnChanged = (equalFontInfo && equalWrappingAlgorithm && !equalWrappingColumn && equalWrappingIndent); this.fontInfo = fontInfo; + this.wrappingAlgorithm = wrappingAlgorithm; this.wrappingColumn = wrappingColumn; this.wrappingIndent = wrappingIndent; @@ -512,7 +515,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { public createLineBreaksComputer(): ILineBreaksComputer { const lineBreaksComputerFactory = ( - usDOMLineBreaksComputerFactory + this.wrappingAlgorithm === 'dom' ? this._domLineBreaksComputerFactory : this._monospaceLineBreaksComputerFactory ); @@ -1457,7 +1460,7 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return false; } - public setWrappingSettings(_fontInfo: FontInfo, _wrappingColumn: number, _wrappingIndent: WrappingIndent): boolean { + public setWrappingSettings(_fontInfo: FontInfo, _wrappingAlgorithm: 'monospace' | 'dom', _wrappingColumn: number, _wrappingIndent: WrappingIndent): boolean { return false; } diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index f6b5d5eeb4f..6bba786da59 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -67,8 +67,9 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } else { const options = this.configuration.options; - const wrappingInfo = options.get(EditorOption.wrappingInfo); const fontInfo = options.get(EditorOption.fontInfo); + const wrappingAlgorithm = options.get(EditorOption.wrappingAlgorithm); + const wrappingInfo = options.get(EditorOption.wrappingInfo); const wrappingIndent = options.get(EditorOption.wrappingIndent); this.lines = new SplitLinesCollection( @@ -77,6 +78,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel monospaceLineBreaksComputerFactory, fontInfo, this.model.getOptions().tabSize, + wrappingAlgorithm, wrappingInfo.wrappingColumn, wrappingIndent ); @@ -153,11 +155,12 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel let restorePreviousViewportStart = false; const options = this.configuration.options; - const wrappingInfo = options.get(EditorOption.wrappingInfo); const fontInfo = options.get(EditorOption.fontInfo); + const wrappingAlgorithm = options.get(EditorOption.wrappingAlgorithm); + const wrappingInfo = options.get(EditorOption.wrappingInfo); const wrappingIndent = options.get(EditorOption.wrappingIndent); - if (this.lines.setWrappingSettings(fontInfo, wrappingInfo.wrappingColumn, wrappingIndent)) { + if (this.lines.setWrappingSettings(fontInfo, wrappingAlgorithm, wrappingInfo.wrappingColumn, wrappingIndent)) { eventsCollector.emit(new viewEvents.ViewFlushedEvent()); eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index 53234a03a3a..7bd6c592366 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -111,6 +111,7 @@ suite('Editor ViewModel - SplitLinesCollection', () => { lineBreaksComputerFactory, fontInfo, model.getOptions().tabSize, + 'monospace', wrappingInfo.wrappingColumn, wrappingIndent ); @@ -752,6 +753,7 @@ suite('SplitLinesCollection', () => { lineBreaksComputerFactory, fontInfo, model.getOptions().tabSize, + 'monospace', wrappingInfo.wrappingColumn, wrappingIndent ); From ebfcd2a3a80cb3585000d143e0f38ac3a8d02db6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 13 Jan 2020 15:21:40 +0100 Subject: [PATCH 195/843] directly add override identifier property schema --- .../common/configurationRegistry.ts | 41 +++++-------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 61590f38fc4..728bb7e66bf 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -8,10 +8,8 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { Registry } from 'vs/platform/registry/common/platform'; import * as types from 'vs/base/common/types'; -import * as strings from 'vs/base/common/strings'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { values } from 'vs/base/common/map'; export const Extensions = { Configuration: 'base.contributions.configuration' @@ -159,7 +157,6 @@ class ConfigurationRegistry implements IConfigurationRegistry { private readonly excludedConfigurationProperties: { [qualifiedKey: string]: IJSONSchema }; private readonly resourceLanguageSettingsSchema: IJSONSchema; private readonly overrideIdentifiers = new Set(); - private overridePropertyPattern: string; private readonly _onDidSchemaChange = new Emitter(); readonly onDidSchemaChange: Event = this._onDidSchemaChange.event; @@ -177,7 +174,6 @@ class ConfigurationRegistry implements IConfigurationRegistry { this.resourceLanguageSettingsSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown editor configuration setting', allowTrailingCommas: true, allowComments: true }; this.configurationProperties = {}; this.excludedConfigurationProperties = {}; - this.overridePropertyPattern = this.computeOverridePropertyPattern(); contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema); } @@ -394,42 +390,27 @@ class ConfigurationRegistry implements IConfigurationRegistry { } private updateOverridePropertyPatternKey(): void { - let patternProperties: IJSONSchema = allSettings.patternProperties[this.overridePropertyPattern]; - if (!patternProperties) { - patternProperties = { + for (const overrideIdentifier of this.overrideIdentifiers) { + const overrideIdentifierProperty = `[${overrideIdentifier}]`; + const resourceLanguagePropertiesSchema: IJSONSchema = { type: 'object', description: nls.localize('overrideSettings.defaultDescription', "Configure editor settings to be overridden for a language."), errorMessage: 'Unknown Identifier. Use language identifiers', - $ref: resourceLanguageSettingsSchemaId + $ref: resourceLanguageSettingsSchemaId, + default: this.defaultOverridesConfigurationNode.properties![overrideIdentifierProperty]?.default }; + allSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema; + applicationSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema; + machineSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema; + machineOverridableSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema; + windowSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema; + resourceSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema; } - - delete allSettings.patternProperties[this.overridePropertyPattern]; - delete applicationSettings.patternProperties[this.overridePropertyPattern]; - delete machineSettings.patternProperties[this.overridePropertyPattern]; - delete machineOverridableSettings.patternProperties[this.overridePropertyPattern]; - delete windowSettings.patternProperties[this.overridePropertyPattern]; - delete resourceSettings.patternProperties[this.overridePropertyPattern]; - - this.overridePropertyPattern = this.computeOverridePropertyPattern(); - - allSettings.patternProperties[this.overridePropertyPattern] = patternProperties; - applicationSettings.patternProperties[this.overridePropertyPattern] = patternProperties; - machineSettings.patternProperties[this.overridePropertyPattern] = patternProperties; - machineOverridableSettings.patternProperties[this.overridePropertyPattern] = patternProperties; - windowSettings.patternProperties[this.overridePropertyPattern] = patternProperties; - resourceSettings.patternProperties[this.overridePropertyPattern] = patternProperties; - this._onDidSchemaChange.fire(); } - - private computeOverridePropertyPattern(): string { - return this.overrideIdentifiers.size > 0 ? OVERRIDE_PATTERN_WITH_SUBSTITUTION.replace('${0}', values(this.overrideIdentifiers).map(identifier => strings.createRegExp(identifier, false).source).join('|')) : OVERRIDE_PROPERTY; - } } const OVERRIDE_PROPERTY = '\\[.*\\]$'; -const OVERRIDE_PATTERN_WITH_SUBSTITUTION = '\\[(${0})\\]$'; export const OVERRIDE_PROPERTY_PATTERN = new RegExp(OVERRIDE_PROPERTY); export function getDefaultValue(type: string | string[] | undefined): any { From 9d2b165a056dd101e1d7abd5d9f3ee773c0991eb Mon Sep 17 00:00:00 2001 From: Luis Oliveira Date: Mon, 13 Jan 2020 14:30:10 +0000 Subject: [PATCH 196/843] Fixing build isssues --- src/vs/base/browser/dom.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index f45c1697c78..2e738925710 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -820,7 +820,7 @@ export function hasParentWithClass(node: HTMLElement, clazz: string, stopAtClazz return !!findParentWithClass(node, clazz, stopAtClazzOrNode); } -export function createStyleSheet(container: HTMLElement | null): HTMLStyleElement { +export function createStyleSheet(container: HTMLElement | null = null): HTMLStyleElement { if (!container) { if ((window as any).monacoShadowRoot) { container = (window as any).monacoShadowRoot.querySelector('head'); @@ -846,7 +846,7 @@ export function createMetaElement(container: HTMLElement = document.getElementsB let _sharedStyleSheet: HTMLStyleElement | null = null; function getSharedStyleSheet(): HTMLStyleElement { if (!_sharedStyleSheet) { - _sharedStyleSheet = createStyleSheet(null); + _sharedStyleSheet = createStyleSheet(); } return _sharedStyleSheet; } From 06a9d488fb9a38171a4ea0bb3679b1a44125395e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 13 Jan 2020 15:46:19 +0100 Subject: [PATCH 197/843] fix compilation --- src/vs/platform/configuration/common/configurationRegistry.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 728bb7e66bf..bad49e688da 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -10,6 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import * as types from 'vs/base/common/types'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { values } from 'vs/base/common/map'; export const Extensions = { Configuration: 'base.contributions.configuration' @@ -390,7 +391,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { } private updateOverridePropertyPatternKey(): void { - for (const overrideIdentifier of this.overrideIdentifiers) { + for (const overrideIdentifier of values(this.overrideIdentifiers)) { const overrideIdentifierProperty = `[${overrideIdentifier}]`; const resourceLanguagePropertiesSchema: IJSONSchema = { type: 'object', From ad41028d8f23204093a994edd00fac2d3d33499b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 Jan 2020 15:49:15 +0100 Subject: [PATCH 198/843] debt - drop untyped events in favor of typed (text files) --- .../api/browser/mainThreadDocuments.ts | 6 +- .../browser/parts/editor/editorStatus.ts | 2 +- .../experiments/common/experimentService.ts | 2 +- .../browser/editors/fileEditorTracker.ts | 4 +- .../editors/textFileSaveErrorHandler.ts | 4 +- .../files/common/editors/fileEditorInput.ts | 10 +- .../browser/languageSurveys.contribution.ts | 2 +- .../browser/telemetry.contribution.ts | 4 +- .../textfile/common/textFileEditorModel.ts | 44 +++++--- .../common/textFileEditorModelManager.ts | 100 +++++++----------- .../services/textfile/common/textfiles.ts | 33 +++--- .../textfile/test/textFileEditorModel.test.ts | 50 ++++----- .../test/textFileEditorModelManager.test.ts | 14 +-- .../api/mainThreadDocumentsAndEditors.test.ts | 6 +- .../api/mainThreadEditors.test.ts | 6 +- 15 files changed, 133 insertions(+), 154 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDocuments.ts b/src/vs/workbench/api/browser/mainThreadDocuments.ts index 3568bd30704..9189cebfb1e 100644 --- a/src/vs/workbench/api/browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/browser/mainThreadDocuments.ts @@ -104,17 +104,17 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { this._toDispose.add(this._modelReferenceCollection); this._toDispose.add(modelService.onModelModeChanged(this._onModelModeChanged, this)); - this._toDispose.add(textFileService.models.onModelSaved(m => { + this._toDispose.add(textFileService.models.onDidSave(m => { if (this._shouldHandleFileEvent(m.resource)) { this._proxy.$acceptModelSaved(m.resource); } })); - this._toDispose.add(textFileService.models.onModelReverted(m => { + this._toDispose.add(textFileService.models.onDidRevert(m => { if (this._shouldHandleFileEvent(m.resource)) { this._proxy.$acceptDirtyStateChanged(m.resource, false); } })); - this._toDispose.add(textFileService.models.onModelDirty(m => { + this._toDispose.add(textFileService.models.onDidChangeDirty(m => { if (this._shouldHandleFileEvent(m.resource)) { this._proxy.$acceptDirtyStateChanged(m.resource, true); } diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index d832143cc04..90644f3691f 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -314,7 +314,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { private registerListeners(): void { this._register(this.editorService.onDidActiveEditorChange(() => this.updateStatusBar())); this._register(this.untitledTextEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r))); - this._register(this.textFileService.models.onModelEncodingChanged(m => this.onResourceEncodingChange((m.resource)))); + this._register(this.textFileService.models.onDidChangeEncoding(m => this.onResourceEncodingChange((m.resource)))); this._register(TabFocus.onDidChangeTabFocus(e => this.onTabFocusModeChange())); } diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index f0eccb33be3..65937cf4908 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -448,7 +448,7 @@ export class ExperimentService extends Disposable implements IExperimentService } }, 250)); - const onSaveHandler = this._register(this.textFileService.models.onModelSaved(m => onModelsSavedWorker.work(m))); + const onSaveHandler = this._register(this.textFileService.models.onDidSave(m => onModelsSavedWorker.work(m))); return ExperimentState.Evaluating; }); } diff --git a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts index 183bae2faa9..591e7bf7b80 100644 --- a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts @@ -61,8 +61,8 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut this._register(this.fileService.onFileChanges(e => this.onFileChanges(e))); // Ensure dirty text file and untitled models are always opened as editors - this._register(this.textFileService.models.onModelDirty(m => this.ensureDirtyFilesAreOpenedWorker.work(m.resource))); - this._register(this.textFileService.models.onModelSaveError(m => this.ensureDirtyFilesAreOpenedWorker.work(m.resource))); + this._register(this.textFileService.models.onDidChangeDirty(m => this.ensureDirtyFilesAreOpenedWorker.work(m.resource))); + this._register(this.textFileService.models.onDidSaveError(m => this.ensureDirtyFilesAreOpenedWorker.work(m.resource))); this._register(this.untitledTextEditorService.onDidChangeDirty(r => this.ensureDirtyFilesAreOpenedWorker.work(r))); // Out of workspace file watchers diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index e94d9a4d23d..7a0d530facb 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -71,8 +71,8 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa } private registerListeners(): void { - this._register(this.textFileService.models.onModelSaved(m => this.onFileSavedOrReverted(m.resource))); - this._register(this.textFileService.models.onModelReverted(m => this.onFileSavedOrReverted(m.resource))); + this._register(this.textFileService.models.onDidSave(m => this.onFileSavedOrReverted(m.resource))); + this._register(this.textFileService.models.onDidRevert(m => this.onFileSavedOrReverted(m.resource))); this._register(this.editorService.onDidActiveEditorChange(() => this.onActiveEditorChanged())); } diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index f6f2db70292..59af8f75b00 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -70,15 +70,15 @@ export class FileEditorInput extends TextEditorInput implements IFileEditorInput private registerListeners(): void { // Dirty changes - this._register(this.textFileService.models.onModelDirty(m => this.onDirtyStateChange(m))); - this._register(this.textFileService.models.onModelSaveError(m => this.onDirtyStateChange(m))); - this._register(this.textFileService.models.onModelSaved(m => this.onDirtyStateChange(m))); - this._register(this.textFileService.models.onModelReverted(m => this.onDirtyStateChange(m))); + this._register(this.textFileService.models.onDidChangeDirty(m => this.onDirtyStateChange(m))); + this._register(this.textFileService.models.onDidSaveError(m => this.onDirtyStateChange(m))); + this._register(this.textFileService.models.onDidSave(m => this.onDirtyStateChange(m))); + this._register(this.textFileService.models.onDidRevert(m => this.onDirtyStateChange(m))); // Label changes this._register(this.labelService.onDidChangeFormatters(() => FileEditorInput.MEMOIZER.clear())); this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(() => FileEditorInput.MEMOIZER.clear())); - this._register(this.textFileService.models.onModelOrphanedChanged(model => this.onModelOrphanedChanged(model))); + this._register(this.textFileService.models.onDidChangeOrphaned(model => this.onModelOrphanedChanged(model))); } private onDirtyStateChange(model: ITextFileEditorModel): void { diff --git a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts index 738a131dd18..4cba8e2b818 100644 --- a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts @@ -62,7 +62,7 @@ class LanguageSurvey extends Disposable { }); }, 250)); - this._register(textFileService.models.onModelSaved(m => onModelsSavedWorker.work(m))); + this._register(textFileService.models.onDidSave(m => onModelsSavedWorker.work(m))); } const lastSessionDate = storageService.get(LAST_SESSION_DATE_KEY, StorageScope.GLOBAL, new Date(0).toDateString()); diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 0682ad8239a..de4f62d26e9 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -124,8 +124,8 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr this._register(configurationTelemetry(telemetryService, configurationService)); // Files Telemetry - this._register(textFileService.models.onModelLoaded(m => this.onTextFileModelLoaded(m))); - this._register(textFileService.models.onModelSaved(m => this.onTextFileModelSaved(m))); + this._register(textFileService.models.onDidLoad(m => this.onTextFileModelLoaded(m))); + this._register(textFileService.models.onDidSave(m => this.onTextFileModelSaved(m))); // Lifecycle this._register(lifecycleService.onShutdown(() => this.dispose())); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 8293240037e..bce63d972b3 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -8,7 +8,7 @@ import { Emitter } from 'vs/base/common/event'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { URI } from 'vs/base/common/uri'; import { assertIsDefined } from 'vs/base/common/types'; -import { ITextFileService, ModelState, ITextFileEditorModel, ISaveErrorHandler, ISaveParticipant, StateChange, ITextFileStreamContent, ILoadOptions, IResolvedTextFileEditorModel, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ModelState, ITextFileEditorModel, ISaveErrorHandler, ISaveParticipant, ITextFileStreamContent, ILoadOptions, IResolvedTextFileEditorModel, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { EncodingMode, IRevertOptions, SaveReason } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -44,15 +44,34 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private static saveParticipant: ISaveParticipant | null; static setSaveParticipant(handler: ISaveParticipant | null): void { TextFileEditorModel.saveParticipant = handler; } + //#region Events + private readonly _onDidChangeContent = this._register(new Emitter()); readonly onDidChangeContent = this._onDidChangeContent.event; - private readonly _onDidChangeState = this._register(new Emitter()); - readonly onDidChangeState = this._onDidChangeState.event; + private readonly _onDidLoad = this._register(new Emitter()); + readonly onDidLoad = this._onDidLoad.event; + + private readonly _onDidSaveError = this._register(new Emitter()); + readonly onDidSaveError = this._onDidSaveError.event; + + private readonly _onDidSave = this._register(new Emitter()); + readonly onDidSave = this._onDidSave.event; + + private readonly _onDidRevert = this._register(new Emitter()); + readonly onDidRevert = this._onDidRevert.event; + + private readonly _onDidChangeEncoding = this._register(new Emitter()); + readonly onDidChangeEncoding = this._onDidChangeEncoding.event; + + private readonly _onDidChangeOrphaned = this._register(new Emitter()); + readonly onDidChangeOrphaned = this._onDidChangeOrphaned.event; private readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; + //#endregion + readonly capabilities = 0; private contentEncoding: string | undefined; // encoding as reported from disk @@ -147,7 +166,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private setOrphaned(orphaned: boolean): void { if (this.inOrphanMode !== orphaned) { this.inOrphanMode = orphaned; - this._onDidChangeState.fire(StateChange.ORPHANED_CHANGE); + this._onDidChangeOrphaned.fire(); } } @@ -215,7 +234,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Emit file change event - this._onDidChangeState.fire(StateChange.REVERTED); + this._onDidRevert.fire(); // Emit dirty change event if (wasDirty) { @@ -372,7 +391,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (this.preferredEncoding) { this.updatePreferredEncoding(this.contentEncoding); // make sure to reflect the real encoding of the file (never out of sync) } else if (oldEncoding !== this.contentEncoding) { - this._onDidChangeState.fire(StateChange.ENCODING); + this._onDidChangeEncoding.fire(); } // Update Existing Model @@ -386,7 +405,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Emit as event - this._onDidChangeState.fire(StateChange.LOADED); + this._onDidLoad.fire(); return this; } @@ -464,7 +483,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Emit event if (wasDirty) { - this._onDidChangeState.fire(StateChange.REVERTED); + this._onDidRevert.fire(); this._onDidChangeDirty.fire(); } } else { @@ -494,7 +513,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Emit as Event if we turned dirty if (!wasDirty) { - this._onDidChangeState.fire(StateChange.DIRTY); this._onDidChangeDirty.fire(); } } @@ -657,7 +675,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.updateLastResolvedFileStat(stat); // Emit Events - this._onDidChangeState.fire(StateChange.SAVED); + this._onDidSave.fire(); this._onDidChangeDirty.fire(); }, error => { this.logService.error(`[text file model] doSave(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource.toString()); @@ -674,7 +692,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.onSaveError(error); // Emit as event - this._onDidChangeState.fire(StateChange.SAVE_ERROR); + this._onDidSaveError.fire(); })); })); } @@ -695,7 +713,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.updateLastResolvedFileStat(stat); // Emit File Saved Event - this._onDidChangeState.fire(StateChange.SAVED); + this._onDidSave.fire(); }, error => onUnexpectedError(error) /* just log any error but do not notify the user since the file was not dirty */)); } @@ -850,7 +868,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.preferredEncoding = encoding; // Emit - this._onDidChangeState.fire(StateChange.ENCODING); + this._onDidChangeEncoding.fire(); } private isNewEncoding(encoding: string | undefined): boolean { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 95f1ae33223..f772a62df09 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -6,8 +6,8 @@ import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { ITextFileEditorModel, ITextFileEditorModelManager, StateChange, IModelLoadOrCreateOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { ITextFileEditorModel, ITextFileEditorModelManager, IModelLoadOrCreateOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ResourceMap } from 'vs/base/common/map'; @@ -18,31 +18,30 @@ import { onUnexpectedError } from 'vs/base/common/errors'; export class TextFileEditorModelManager extends Disposable implements ITextFileEditorModelManager { - private readonly _onModelLoaded = this._register(new Emitter()); - readonly onModelLoaded = this._onModelLoaded.event; + private readonly _onDidLoad = this._register(new Emitter()); + readonly onDidLoad = this._onDidLoad.event; - private readonly _onModelDirty = this._register(new Emitter()); - readonly onModelDirty = this._onModelDirty.event; + private readonly _onDidChangeDirty = this._register(new Emitter()); + readonly onDidChangeDirty = this._onDidChangeDirty.event; - private readonly _onModelSaveError = this._register(new Emitter()); - readonly onModelSaveError = this._onModelSaveError.event; + private readonly _onDidSaveError = this._register(new Emitter()); + readonly onDidSaveError = this._onDidSaveError.event; - private readonly _onModelSaved = this._register(new Emitter()); - readonly onModelSaved = this._onModelSaved.event; + private readonly _onDidSave = this._register(new Emitter()); + readonly onDidSave = this._onDidSave.event; - private readonly _onModelReverted = this._register(new Emitter()); - readonly onModelReverted = this._onModelReverted.event; + private readonly _onDidRevert = this._register(new Emitter()); + readonly onDidRevert = this._onDidRevert.event; - private readonly _onModelEncodingChanged = this._register(new Emitter()); - readonly onModelEncodingChanged = this._onModelEncodingChanged.event; + private readonly _onDidChangeEncoding = this._register(new Emitter()); + readonly onDidChangeEncoding = this._onDidChangeEncoding.event; - private readonly _onModelOrphanedChanged = this._register(new Emitter()); - readonly onModelOrphanedChanged = this._onModelOrphanedChanged.event; + private readonly _onDidChangeOrphaned = this._register(new Emitter()); + readonly onDidChangeOrphaned = this._onDidChangeOrphaned.event; - private readonly mapResourceToDisposeListener = new ResourceMap(); - private readonly mapResourceToStateChangeListener = new ResourceMap(); - private readonly mapResourceToModelContentChangeListener = new ResourceMap(); private readonly mapResourceToModel = new ResourceMap(); + private readonly mapResourceToModelListeners = new ResourceMap(); + private readonly mapResourceToDisposeListener = new ResourceMap(); private readonly mapResourceToPendingModelLoaders = new ResourceMap>(); private readonly modelLoadQueue = new ResourceQueue(); @@ -128,32 +127,17 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE const newModel = model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : undefined, options ? options.mode : undefined); modelPromise = model.load(options); - // Install state change listener - this.mapResourceToStateChangeListener.set(resource, model.onDidChangeState(state => { - switch (state) { - case StateChange.LOADED: - this._onModelLoaded.fire(newModel); - break; - case StateChange.DIRTY: - this._onModelDirty.fire(newModel); - break; - case StateChange.SAVE_ERROR: - this._onModelSaveError.fire(newModel); - break; - case StateChange.SAVED: - this._onModelSaved.fire(newModel); - break; - case StateChange.REVERTED: - this._onModelReverted.fire(newModel); - break; - case StateChange.ENCODING: - this._onModelEncodingChanged.fire(newModel); - break; - case StateChange.ORPHANED_CHANGE: - this._onModelOrphanedChanged.fire(newModel); - break; - } - })); + // Install model listeners + const listeners = new DisposableStore(); + listeners.add(model.onDidLoad(() => this._onDidLoad.fire(newModel))); + listeners.add(model.onDidChangeDirty(() => this._onDidChangeDirty.fire(newModel))); + listeners.add(model.onDidSaveError(() => this._onDidSaveError.fire(newModel))); + listeners.add(model.onDidSave(() => this._onDidSave.fire(newModel))); + listeners.add(model.onDidRevert(() => this._onDidRevert.fire(newModel))); + listeners.add(model.onDidChangeEncoding(() => this._onDidChangeEncoding.fire(newModel))); + listeners.add(model.onDidChangeOrphaned(() => this._onDidChangeOrphaned.fire(newModel))); + + this.mapResourceToModelListeners.set(resource, listeners); } // Store pending loads to avoid race conditions @@ -167,7 +151,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // Model can be dirty if a backup was restored, so we make sure to have this event delivered if (resolvedModel.isDirty()) { - this._onModelDirty.fire(resolvedModel); + this._onDidChangeDirty.fire(resolvedModel); } // Remove from pending loads @@ -236,16 +220,10 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE this.mapResourceToDisposeListener.delete(resource); } - const stateChangeListener = this.mapResourceToStateChangeListener.get(resource); - if (stateChangeListener) { - dispose(stateChangeListener); - this.mapResourceToStateChangeListener.delete(resource); - } - - const modelContentChangeListener = this.mapResourceToModelContentChangeListener.get(resource); - if (modelContentChangeListener) { - dispose(modelContentChangeListener); - this.mapResourceToModelContentChangeListener.delete(resource); + const modelListener = this.mapResourceToModelListeners.get(resource); + if (modelListener) { + dispose(modelListener); + this.mapResourceToModelListeners.delete(resource); } } @@ -259,13 +237,9 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE this.mapResourceToDisposeListener.forEach(l => l.dispose()); this.mapResourceToDisposeListener.clear(); - // dispose the state change listeners - this.mapResourceToStateChangeListener.forEach(l => l.dispose()); - this.mapResourceToStateChangeListener.clear(); - - // dispose the model content change listeners - this.mapResourceToModelContentChangeListener.forEach(l => l.dispose()); - this.mapResourceToModelContentChangeListener.clear(); + // dispose the model change listeners + this.mapResourceToModelListeners.forEach(l => l.dispose()); + this.mapResourceToModelListeners.clear(); } disposeModel(model: TextFileEditorModel): void { diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 561aa04380b..f32f2fd796c 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -275,16 +275,6 @@ export const enum ModelState { ERROR } -export const enum StateChange { - LOADED, - DIRTY, - SAVE_ERROR, - SAVED, - REVERTED, - ENCODING, - ORPHANED_CHANGE -} - export interface ITextFileOperationResult { results: IResult[]; } @@ -362,14 +352,13 @@ export interface IModelLoadOrCreateOptions { export interface ITextFileEditorModelManager { - readonly onModelEncodingChanged: Event; - readonly onModelOrphanedChanged: Event; - - readonly onModelLoaded: Event; - readonly onModelDirty: Event; - readonly onModelSaveError: Event; - readonly onModelSaved: Event; - readonly onModelReverted: Event; + readonly onDidLoad: Event; + readonly onDidChangeDirty: Event; + readonly onDidSaveError: Event; + readonly onDidSave: Event; + readonly onDidRevert: Event; + readonly onDidChangeEncoding: Event; + readonly onDidChangeOrphaned: Event; get(resource: URI): ITextFileEditorModel | undefined; @@ -407,7 +396,13 @@ export interface ILoadOptions { export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport, IModeSupport, IWorkingCopy { - readonly onDidChangeState: Event; + readonly onDidChangeContent: Event; + readonly onDidLoad: Event; + readonly onDidSaveError: Event; + readonly onDidSave: Event; + readonly onDidRevert: Event; + readonly onDidChangeEncoding: Event; + readonly onDidChangeOrphaned: Event; hasState(state: ModelState): boolean; diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index a3b647521f7..4fac27a7c43 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { EncodingMode } from 'vs/workbench/common/editor'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { ITextFileService, ModelState, StateChange, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ModelState, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { workbenchInstantiationService, TestTextFileService, createFileInput, TestFileService } from 'vs/workbench/test/workbenchTestServices'; import { toResource } from 'vs/base/test/common/utils'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; @@ -95,10 +95,8 @@ suite('Files - TextFileEditorModel', () => { assert.equal(accessor.workingCopyService.isDirty(model.resource), true); let savedEvent = false; - model.onDidChangeState(e => { - if (e === StateChange.SAVED) { - savedEvent = true; - } + model.onDidSave(e => { + savedEvent = true; }); let workingCopyEvent = false; @@ -132,10 +130,8 @@ suite('Files - TextFileEditorModel', () => { await model.load(); let savedEvent = false; - model.onDidChangeState(e => { - if (e === StateChange.SAVED) { - savedEvent = true; - } + model.onDidSave(e => { + savedEvent = true; }); let workingCopyEvent = false; @@ -206,8 +202,12 @@ suite('Files - TextFileEditorModel', () => { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8', undefined); assert.ok(model.hasState(ModelState.SAVED)); - model.onDidChangeState(e => { - assert.ok(e !== StateChange.DIRTY && e !== StateChange.SAVED); + model.onDidSave(e => { + assert.fail(); + }); + + model.onDidChangeDirty(e => { + assert.fail(); }); await model.load(); @@ -234,10 +234,8 @@ suite('Files - TextFileEditorModel', () => { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - model.onDidChangeState(e => { - if (e === StateChange.REVERTED) { - eventCounter++; - } + model.onDidRevert(e => { + eventCounter++; }); let workingCopyEvent = false; @@ -271,10 +269,8 @@ suite('Files - TextFileEditorModel', () => { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - model.onDidChangeState(e => { - if (e === StateChange.REVERTED) { - eventCounter++; - } + model.onDidRevert(e => { + eventCounter++; }); let workingCopyEvent = false; @@ -331,10 +327,8 @@ suite('Files - TextFileEditorModel', () => { await model.revert({ soft: true }); assert.ok(!model.isDirty()); - model.onDidChangeState(e => { - if (e === StateChange.DIRTY) { - eventCounter++; - } + model.onDidChangeDirty(e => { + eventCounter++; }); let workingCopyEvent = false; @@ -416,12 +410,10 @@ suite('Files - TextFileEditorModel', () => { let eventCounter = 0; const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - model.onDidChangeState(e => { - if (e === StateChange.SAVED) { - assert.equal(snapshotToString(model.createSnapshot()!), 'bar'); - assert.ok(!model.isDirty()); - eventCounter++; - } + model.onDidSave(e => { + assert.equal(snapshotToString(model.createSnapshot()!), 'bar'); + assert.ok(!model.isDirty()); + eventCounter++; }); TextFileEditorModel.setSaveParticipant({ diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts index 4d5ee1fcee7..b59f1e7b12e 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -127,7 +127,7 @@ suite('Files - TextFileEditorModelManager', () => { model3.dispose(); }); - test('events', async function () { + test('pasero events', async function () { const manager: TextFileEditorModelManager = instantiationService.createInstance(TextFileEditorModelManager); const resource1 = toResource.call(this, '/path/index.txt'); @@ -139,31 +139,31 @@ suite('Files - TextFileEditorModelManager', () => { let savedCounter = 0; let encodingCounter = 0; - manager.onModelLoaded(model => { + manager.onDidLoad(model => { if (model.resource.toString() === resource1.toString()) { loadedCounter++; } }); - manager.onModelDirty(model => { + manager.onDidChangeDirty(model => { if (model.resource.toString() === resource1.toString()) { dirtyCounter++; } }); - manager.onModelReverted(model => { + manager.onDidRevert(model => { if (model.resource.toString() === resource1.toString()) { revertedCounter++; } }); - manager.onModelSaved(model => { + manager.onDidSave(model => { if (model.resource.toString() === resource1.toString()) { savedCounter++; } }); - manager.onModelEncodingChanged(model => { + manager.onDidChangeEncoding(model => { if (model.resource.toString() === resource1.toString()) { encodingCounter++; } @@ -189,7 +189,7 @@ suite('Files - TextFileEditorModelManager', () => { model2.dispose(); await model1.revert(); - assert.equal(dirtyCounter, 2); + assert.equal(dirtyCounter, 4); assert.equal(revertedCounter, 1); assert.equal(savedCounter, 1); assert.equal(encodingCounter, 2); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts index 78c73141ae0..12d00d9f733 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadDocumentsAndEditors.test.ts @@ -49,9 +49,9 @@ suite('MainThreadDocumentsAndEditors', () => { textFileService = new class extends mock() { isDirty() { return false; } models = { - onModelSaved: Event.None, - onModelReverted: Event.None, - onModelDirty: Event.None, + onDidSave: Event.None, + onDidRevert: Event.None, + onDidChangeDirty: Event.None }; }; const workbenchEditorService = new TestEditorService(); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts index 84705671d3f..8e7888ba8ce 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -73,9 +73,9 @@ suite('MainThreadEditors', () => { return Promise.resolve(Object.create(null)); } models = { - onModelSaved: Event.None, - onModelReverted: Event.None, - onModelDirty: Event.None, + onDidSave: Event.None, + onDidRevert: Event.None, + onDidChangeDirty: Event.None }; }; const workbenchEditorService = new TestEditorService(); From 420fa189244b1e40d2299265592f79fdee6cfba4 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 13 Jan 2020 15:54:10 +0100 Subject: [PATCH 199/843] :lipstick: --- extensions/git/src/fileSystemProvider.ts | 31 +++++++++++++----------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/extensions/git/src/fileSystemProvider.ts b/extensions/git/src/fileSystemProvider.ts index 9687dbf59de..2799b95bda9 100644 --- a/extensions/git/src/fileSystemProvider.ts +++ b/extensions/git/src/fileSystemProvider.ts @@ -18,6 +18,21 @@ interface CacheRow { const THREE_MINUTES = 1000 * 60 * 3; const FIVE_MINUTES = 1000 * 60 * 5; +function sanitizeRef(ref: string, path: string, repository: Repository): string { + if (ref === '~') { + const fileUri = Uri.file(path); + const uriString = fileUri.toString(); + const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); + return indexStatus ? '' : 'HEAD'; + } + + if (/^~\d$/.test(ref)) { + return `:${ref[1]}`; + } + + return ref; +} + export class GitFileSystemProvider implements FileSystemProvider { private _onDidChangeFile = new EventEmitter(); @@ -126,7 +141,7 @@ export class GitFileSystemProvider implements FileSystemProvider { let size = 0; try { - const details = await repository.getObjectDetails(this.fixRef(ref, path, repository), path); + const details = await repository.getObjectDetails(sanitizeRef(ref, path, repository), path); size = details.size; } catch { // noop @@ -173,7 +188,7 @@ export class GitFileSystemProvider implements FileSystemProvider { this.cache.set(uri.toString(), cacheValue); try { - return await repository.buffer(this.fixRef(ref, path, repository), path); + return await repository.buffer(sanitizeRef(ref, path, repository), path); } catch (err) { return new Uint8Array(0); } @@ -194,16 +209,4 @@ export class GitFileSystemProvider implements FileSystemProvider { dispose(): void { this.disposables.forEach(d => d.dispose()); } - - private fixRef(ref: string, path: string, repository: Repository): string { - if (ref === '~') { - const fileUri = Uri.file(path); - const uriString = fileUri.toString(); - const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); - return indexStatus ? '' : 'HEAD'; - } else if (/^~\d$/.test(ref)) { - return `:${ref[1]}`; - } - return ref; - } } From 76c72ffc947fa8dfe9180409f3b204541e05b10b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 Jan 2020 16:01:05 +0100 Subject: [PATCH 200/843] telemetry - restore back load/save reason for files --- .../api/browser/mainThreadDocuments.ts | 6 +++--- .../experiments/common/experimentService.ts | 2 +- .../editors/textFileSaveErrorHandler.ts | 2 +- .../files/common/editors/fileEditorInput.ts | 2 +- .../browser/languageSurveys.contribution.ts | 2 +- .../browser/telemetry.contribution.ts | 18 +++++++++--------- .../textfile/common/textFileEditorModel.ts | 16 ++++++++-------- .../common/textFileEditorModelManager.ts | 10 +++++----- .../services/textfile/common/textfiles.ts | 18 ++++++++++++++---- .../test/textFileEditorModelManager.test.ts | 4 ++-- 10 files changed, 45 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDocuments.ts b/src/vs/workbench/api/browser/mainThreadDocuments.ts index 9189cebfb1e..8b624063795 100644 --- a/src/vs/workbench/api/browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/browser/mainThreadDocuments.ts @@ -104,9 +104,9 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { this._toDispose.add(this._modelReferenceCollection); this._toDispose.add(modelService.onModelModeChanged(this._onModelModeChanged, this)); - this._toDispose.add(textFileService.models.onDidSave(m => { - if (this._shouldHandleFileEvent(m.resource)) { - this._proxy.$acceptModelSaved(m.resource); + this._toDispose.add(textFileService.models.onDidSave(e => { + if (this._shouldHandleFileEvent(e.model.resource)) { + this._proxy.$acceptModelSaved(e.model.resource); } })); this._toDispose.add(textFileService.models.onDidRevert(m => { diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index 65937cf4908..226b389f3f5 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -448,7 +448,7 @@ export class ExperimentService extends Disposable implements IExperimentService } }, 250)); - const onSaveHandler = this._register(this.textFileService.models.onDidSave(m => onModelsSavedWorker.work(m))); + const onSaveHandler = this._register(this.textFileService.models.onDidSave(e => onModelsSavedWorker.work(e.model))); return ExperimentState.Evaluating; }); } diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index 7a0d530facb..02b32c67006 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -71,7 +71,7 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa } private registerListeners(): void { - this._register(this.textFileService.models.onDidSave(m => this.onFileSavedOrReverted(m.resource))); + this._register(this.textFileService.models.onDidSave(e => this.onFileSavedOrReverted(e.model.resource))); this._register(this.textFileService.models.onDidRevert(m => this.onFileSavedOrReverted(m.resource))); this._register(this.editorService.onDidActiveEditorChange(() => this.onActiveEditorChanged())); } diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 59af8f75b00..e21bfa9fec3 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -72,7 +72,7 @@ export class FileEditorInput extends TextEditorInput implements IFileEditorInput // Dirty changes this._register(this.textFileService.models.onDidChangeDirty(m => this.onDirtyStateChange(m))); this._register(this.textFileService.models.onDidSaveError(m => this.onDirtyStateChange(m))); - this._register(this.textFileService.models.onDidSave(m => this.onDirtyStateChange(m))); + this._register(this.textFileService.models.onDidSave(e => this.onDirtyStateChange(e.model))); this._register(this.textFileService.models.onDidRevert(m => this.onDirtyStateChange(m))); // Label changes diff --git a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts index 4cba8e2b818..e196f2c4ecd 100644 --- a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts @@ -62,7 +62,7 @@ class LanguageSurvey extends Disposable { }); }, 250)); - this._register(textFileService.models.onDidSave(m => onModelsSavedWorker.work(m))); + this._register(textFileService.models.onDidSave(e => onModelsSavedWorker.work(e.model))); } const lastSessionDate = storageService.get(LAST_SESSION_DATE_KEY, StorageScope.GLOBAL, new Date(0).toDateString()); diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index de4f62d26e9..a01ba225296 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -19,7 +19,7 @@ import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; import { configurationTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ITextFileModelSaveEvent, ITextFileModelLoadEvent } from 'vs/workbench/services/textfile/common/textfiles'; import { extname, basename, isEqual, isEqualOrParent, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; @@ -124,15 +124,15 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr this._register(configurationTelemetry(telemetryService, configurationService)); // Files Telemetry - this._register(textFileService.models.onDidLoad(m => this.onTextFileModelLoaded(m))); - this._register(textFileService.models.onDidSave(m => this.onTextFileModelSaved(m))); + this._register(textFileService.models.onDidLoad(e => this.onTextFileModelLoaded(e))); + this._register(textFileService.models.onDidSave(e => this.onTextFileModelSaved(e))); // Lifecycle this._register(lifecycleService.onShutdown(() => this.dispose())); } - private onTextFileModelLoaded(model: ITextFileEditorModel): void { - const settingsType = this.getTypeIfSettings(model.resource); + private onTextFileModelLoaded(e: ITextFileModelLoadEvent): void { + const settingsType = this.getTypeIfSettings(e.model.resource); if (settingsType) { type SettingsReadClassification = { settingsType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; @@ -142,12 +142,12 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr } else { type FileGetClassification = {} & FileTelemetryDataFragment; - this.telemetryService.publicLog2('fileGet', this.getTelemetryData(model.resource)); + this.telemetryService.publicLog2('fileGet', this.getTelemetryData(e.model.resource, e.reason)); } } - private onTextFileModelSaved(model: ITextFileEditorModel): void { - const settingsType = this.getTypeIfSettings(model.resource); + private onTextFileModelSaved(e: ITextFileModelSaveEvent): void { + const settingsType = this.getTypeIfSettings(e.model.resource); if (settingsType) { type SettingsWrittenClassification = { settingsType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; @@ -155,7 +155,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr this.telemetryService.publicLog2<{ settingsType: string }, SettingsWrittenClassification>('settingsWritten', { settingsType }); // Do not log write to user settings.json and .vscode folder as a filePUT event as it ruins our JSON usage data } else { type FilePutClassfication = {} & FileTelemetryDataFragment; - this.telemetryService.publicLog2('filePUT', this.getTelemetryData(model.resource)); + this.telemetryService.publicLog2('filePUT', this.getTelemetryData(e.model.resource, e.reason)); } } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index bce63d972b3..7abf65e410c 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -8,7 +8,7 @@ import { Emitter } from 'vs/base/common/event'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { URI } from 'vs/base/common/uri'; import { assertIsDefined } from 'vs/base/common/types'; -import { ITextFileService, ModelState, ITextFileEditorModel, ISaveErrorHandler, ISaveParticipant, ITextFileStreamContent, ILoadOptions, IResolvedTextFileEditorModel, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ModelState, ITextFileEditorModel, ISaveErrorHandler, ISaveParticipant, ITextFileStreamContent, ILoadOptions, IResolvedTextFileEditorModel, ITextFileSaveOptions, LoadReason } from 'vs/workbench/services/textfile/common/textfiles'; import { EncodingMode, IRevertOptions, SaveReason } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -49,13 +49,13 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private readonly _onDidChangeContent = this._register(new Emitter()); readonly onDidChangeContent = this._onDidChangeContent.event; - private readonly _onDidLoad = this._register(new Emitter()); + private readonly _onDidLoad = this._register(new Emitter()); readonly onDidLoad = this._onDidLoad.event; private readonly _onDidSaveError = this._register(new Emitter()); readonly onDidSaveError = this._onDidSaveError.event; - private readonly _onDidSave = this._register(new Emitter()); + private readonly _onDidSave = this._register(new Emitter()); readonly onDidSave = this._onDidSave.event; private readonly _onDidRevert = this._register(new Emitter()); @@ -405,7 +405,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Emit as event - this._onDidLoad.fire(); + this._onDidLoad.fire(options?.reason ?? LoadReason.OTHER); return this; } @@ -637,7 +637,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // - the model is not in orphan mode (because in that case we know the file does not exist on disk) // - the model version did not change due to save participants running if (options.force && !this.dirty && !this.inOrphanMode && options.reason === SaveReason.EXPLICIT && versionId === newVersionId) { - return this.doTouch(newVersionId); + return this.doTouch(newVersionId, options.reason); } // update versionId with its new value (if pre-save changes happened) @@ -675,7 +675,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.updateLastResolvedFileStat(stat); // Emit Events - this._onDidSave.fire(); + this._onDidSave.fire(options.reason ?? SaveReason.EXPLICIT); this._onDidChangeDirty.fire(); }, error => { this.logService.error(`[text file model] doSave(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource.toString()); @@ -697,7 +697,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil })); } - private doTouch(versionId: number): Promise { + private doTouch(versionId: number, reason: SaveReason): Promise { if (!this.isResolved()) { return Promise.resolve(); } @@ -713,7 +713,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.updateLastResolvedFileStat(stat); // Emit File Saved Event - this._onDidSave.fire(); + this._onDidSave.fire(reason); }, error => onUnexpectedError(error) /* just log any error but do not notify the user since the file was not dirty */)); } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index f772a62df09..4fbc86a0d1f 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -7,7 +7,7 @@ import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { ITextFileEditorModel, ITextFileEditorModelManager, IModelLoadOrCreateOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileEditorModel, ITextFileEditorModelManager, IModelLoadOrCreateOptions, ITextFileModelLoadEvent, ITextFileModelSaveEvent } from 'vs/workbench/services/textfile/common/textfiles'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ResourceMap } from 'vs/base/common/map'; @@ -18,7 +18,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; export class TextFileEditorModelManager extends Disposable implements ITextFileEditorModelManager { - private readonly _onDidLoad = this._register(new Emitter()); + private readonly _onDidLoad = this._register(new Emitter()); readonly onDidLoad = this._onDidLoad.event; private readonly _onDidChangeDirty = this._register(new Emitter()); @@ -27,7 +27,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE private readonly _onDidSaveError = this._register(new Emitter()); readonly onDidSaveError = this._onDidSaveError.event; - private readonly _onDidSave = this._register(new Emitter()); + private readonly _onDidSave = this._register(new Emitter()); readonly onDidSave = this._onDidSave.event; private readonly _onDidRevert = this._register(new Emitter()); @@ -129,10 +129,10 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // Install model listeners const listeners = new DisposableStore(); - listeners.add(model.onDidLoad(() => this._onDidLoad.fire(newModel))); + listeners.add(model.onDidLoad(reason => this._onDidLoad.fire({ model: newModel, reason }))); listeners.add(model.onDidChangeDirty(() => this._onDidChangeDirty.fire(newModel))); listeners.add(model.onDidSaveError(() => this._onDidSaveError.fire(newModel))); - listeners.add(model.onDidSave(() => this._onDidSave.fire(newModel))); + listeners.add(model.onDidSave(reason => this._onDidSave.fire({ model: newModel, reason }))); listeners.add(model.onDidRevert(() => this._onDidRevert.fire(newModel))); listeners.add(model.onDidChangeEncoding(() => this._onDidChangeEncoding.fire(newModel))); listeners.add(model.onDidChangeOrphaned(() => this._onDidChangeOrphaned.fire(newModel))); diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index f32f2fd796c..89ce41bdbaa 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -350,12 +350,22 @@ export interface IModelLoadOrCreateOptions { allowBinary?: boolean; } +export interface ITextFileModelSaveEvent { + model: ITextFileEditorModel; + reason: SaveReason; +} + +export interface ITextFileModelLoadEvent { + model: ITextFileEditorModel; + reason: LoadReason; +} + export interface ITextFileEditorModelManager { - readonly onDidLoad: Event; + readonly onDidLoad: Event; readonly onDidChangeDirty: Event; readonly onDidSaveError: Event; - readonly onDidSave: Event; + readonly onDidSave: Event; readonly onDidRevert: Event; readonly onDidChangeEncoding: Event; readonly onDidChangeOrphaned: Event; @@ -397,9 +407,9 @@ export interface ILoadOptions { export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport, IModeSupport, IWorkingCopy { readonly onDidChangeContent: Event; - readonly onDidLoad: Event; + readonly onDidLoad: Event; readonly onDidSaveError: Event; - readonly onDidSave: Event; + readonly onDidSave: Event; readonly onDidRevert: Event; readonly onDidChangeEncoding: Event; readonly onDidChangeOrphaned: Event; diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts index b59f1e7b12e..3eab2ccf1aa 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -139,7 +139,7 @@ suite('Files - TextFileEditorModelManager', () => { let savedCounter = 0; let encodingCounter = 0; - manager.onDidLoad(model => { + manager.onDidLoad(({ model }) => { if (model.resource.toString() === resource1.toString()) { loadedCounter++; } @@ -157,7 +157,7 @@ suite('Files - TextFileEditorModelManager', () => { } }); - manager.onDidSave(model => { + manager.onDidSave(({ model }) => { if (model.resource.toString() === resource1.toString()) { savedCounter++; } From a62805844ef4b2c0bf22b069bcf136ac064feded Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 13 Jan 2020 07:45:08 -0800 Subject: [PATCH 201/843] Expose IExtHostTerminalService.getDefaultShellArgs internally Fixes #88280 --- src/vs/workbench/api/common/extHostTerminalService.ts | 6 ++++++ src/vs/workbench/api/node/extHostTerminalService.ts | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index f5635b83d23..6314faeadf4 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -32,6 +32,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape { createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal; attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void; getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string; + getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string; } export const IExtHostTerminalService = createDecorator('IExtHostTerminalService'); @@ -321,6 +322,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ public abstract createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal; public abstract createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal; public abstract getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string; + public abstract getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string; public abstract $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise; public abstract $requestAvailableShells(): Promise; public abstract $requestDefaultShellAndArgs(useAutomationShell: boolean): Promise; @@ -595,6 +597,10 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService { throw new Error('Not implemented'); } + public getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string { + throw new Error('Not implemented'); + } + public $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { throw new Error('Not implemented'); } diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 8d2f73a9263..1eda3462786 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -78,7 +78,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { ); } - private _getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string { + public getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string { const fetchSetting = (key: string): { userValue: string | string[] | undefined, value: string | string[] | undefined, defaultValue: string | string[] | undefined } => { const setting = configProvider .getConfiguration(key.substr(0, key.lastIndexOf('.'))) @@ -137,7 +137,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { const configProvider = await this._extHostConfiguration.getConfigProvider(); if (!shellLaunchConfig.executable) { shellLaunchConfig.executable = this.getDefaultShell(false, configProvider); - shellLaunchConfig.args = this._getDefaultShellArgs(false, configProvider); + shellLaunchConfig.args = this.getDefaultShellArgs(false, configProvider); } else { if (this._variableResolver) { shellLaunchConfig.executable = this._variableResolver.resolve(this._lastActiveWorkspace, shellLaunchConfig.executable); @@ -208,7 +208,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { const configProvider = await this._extHostConfiguration.getConfigProvider(); return Promise.resolve({ shell: this.getDefaultShell(useAutomationShell, configProvider), - args: this._getDefaultShellArgs(useAutomationShell, configProvider) + args: this.getDefaultShellArgs(useAutomationShell, configProvider) }); } From 2a3dc1edace0e365e79019de0fd7c7f0eb10d51c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 13 Jan 2020 16:59:08 +0100 Subject: [PATCH 202/843] fix tests --- src/vs/workbench/api/browser/mainThreadDocuments.ts | 7 +------ .../textfile/test/textFileEditorModelManager.test.ts | 12 +++++++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDocuments.ts b/src/vs/workbench/api/browser/mainThreadDocuments.ts index 8b624063795..7be61f10e10 100644 --- a/src/vs/workbench/api/browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/browser/mainThreadDocuments.ts @@ -109,14 +109,9 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { this._proxy.$acceptModelSaved(e.model.resource); } })); - this._toDispose.add(textFileService.models.onDidRevert(m => { - if (this._shouldHandleFileEvent(m.resource)) { - this._proxy.$acceptDirtyStateChanged(m.resource, false); - } - })); this._toDispose.add(textFileService.models.onDidChangeDirty(m => { if (this._shouldHandleFileEvent(m.resource)) { - this._proxy.$acceptDirtyStateChanged(m.resource, true); + this._proxy.$acceptDirtyStateChanged(m.resource, m.isDirty()); } })); diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts index 3eab2ccf1aa..8a27ddcad42 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -134,7 +134,8 @@ suite('Files - TextFileEditorModelManager', () => { const resource2 = toResource.call(this, '/path/other.txt'); let loadedCounter = 0; - let dirtyCounter = 0; + let gotDirtyCounter = 0; + let gotNonDirtyCounter = 0; let revertedCounter = 0; let savedCounter = 0; let encodingCounter = 0; @@ -147,7 +148,11 @@ suite('Files - TextFileEditorModelManager', () => { manager.onDidChangeDirty(model => { if (model.resource.toString() === resource1.toString()) { - dirtyCounter++; + if (model.isDirty()) { + gotDirtyCounter++; + } else { + gotNonDirtyCounter++; + } } }); @@ -189,7 +194,8 @@ suite('Files - TextFileEditorModelManager', () => { model2.dispose(); await model1.revert(); - assert.equal(dirtyCounter, 4); + assert.equal(gotDirtyCounter, 2); + assert.equal(gotNonDirtyCounter, 2); assert.equal(revertedCounter, 1); assert.equal(savedCounter, 1); assert.equal(encodingCounter, 2); From 08c04a487dd392561e8bf74bc9294135aaf7d8ba Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 13 Jan 2020 17:05:59 +0100 Subject: [PATCH 203/843] add accessibility provider for refactor preview, #88554 --- .../contrib/bulkEdit/browser/bulkEditPane.ts | 3 +- .../contrib/bulkEdit/browser/bulkEditTree.ts | 69 ++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index c4456386b4f..3f3d562d5d1 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -6,7 +6,7 @@ import 'vs/css!./bulkEdit'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; -import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; +import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; @@ -96,6 +96,7 @@ export class BulkEditPane extends ViewPane { [new TextEditElementRenderer(), this._instaService.createInstance(FileElementRenderer, resourceLabels)], this._instaService.createInstance(BulkEditDataSource), { + accessibilityProvider: this._instaService.createInstance(BulkEditAccessibilityProvider), identityProvider: new BulkEditIdentityProvider(), expandOnlyOnTwistieClick: true } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 03746a0a7bc..188c0569442 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -19,6 +19,7 @@ import { BulkFileOperations, BulkFileOperation, BulkFileOperationType, BulkTextE import { FileKind } from 'vs/platform/files/common/files'; import { localize } from 'vs/nls'; import { ILabelService } from 'vs/platform/label/common/label'; +import type { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; // --- VIEW MODEL @@ -106,6 +107,72 @@ export class BulkEditDataSource implements IAsyncDataSource { + + constructor(@ILabelService private readonly _labelService: ILabelService) { } + + getAriaLabel(element: BulkEditElement): string | null { + if (element instanceof FileElement) { + if (element.edit.textEdits.length > 0) { + if (element.edit.type & BulkFileOperationType.Rename && element.edit.newUri) { + return localize( + 'area.renameAndEdit', "Renaming {0} to {1}, also making text edits", + this._labelService.getUriLabel(element.edit.uri, { relative: true }), this._labelService.getUriLabel(element.edit.newUri, { relative: true }) + ); + + } else if (element.edit.type & BulkFileOperationType.Create) { + return localize( + 'area.createAndEdit', "Creating {0}, also making text edits", + this._labelService.getUriLabel(element.edit.uri, { relative: true }) + ); + + } else if (element.edit.type & BulkFileOperationType.Delete) { + return localize( + 'area.deleteAndEdit', "Deleting {0}, also making text edits", + this._labelService.getUriLabel(element.edit.uri, { relative: true }), + ); + } + } else { + if (element.edit.type & BulkFileOperationType.Rename && element.edit.newUri) { + return localize( + 'area.rename', "Renaming {0} to {1}", + this._labelService.getUriLabel(element.edit.uri, { relative: true }), this._labelService.getUriLabel(element.edit.newUri, { relative: true }) + ); + + } else if (element.edit.type & BulkFileOperationType.Create) { + return localize( + 'area.create', "Creating {0}", + this._labelService.getUriLabel(element.edit.uri, { relative: true }) + ); + + } else if (element.edit.type & BulkFileOperationType.Delete) { + return localize( + 'area.delete', "Deleting {0}", + this._labelService.getUriLabel(element.edit.uri, { relative: true }), + ); + } + } + } + + if (element instanceof TextEditElement) { + if (element.selecting.length > 0 && element.inserting.length > 0) { + // edit: replace + return localize('aria.replace', "line {0}, replacing {1} with {0}", element.edit.edit.range.startLineNumber, element.selecting, element.inserting); + } else if (element.selecting.length > 0 && element.inserting.length === 0) { + // edit: delete + return localize('aria.del', "line {0}, removing {1}", element.edit.edit.range.startLineNumber, element.selecting); + } else if (element.selecting.length === 0 && element.inserting.length > 0) { + // edit: insert + return localize('aria.insert', "line {0}, inserting {1}", element.edit.edit.range.startLineNumber, element.selecting); + } + } + + return null; + } +} + // --- IDENT export class BulkEditIdentityProvider implements IIdentityProvider { @@ -183,7 +250,7 @@ class FileElementTemplate { if (element.edit.type & BulkFileOperationType.Create) { this._details.innerText = localize('detail.create', "(creating)"); - } else if (element.edit.type & BulkFileOperationType.Create) { + } else if (element.edit.type & BulkFileOperationType.Delete) { this._details.innerText = localize('detail.del', "(deleting)"); } else { this._details.innerText = ''; From 9bd40be50e1fb343b4653e56cca498d313c9a543 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 13 Jan 2020 17:16:23 +0100 Subject: [PATCH 204/843] fix #88560 --- src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 3f3d562d5d1..13e60a3218a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -98,7 +98,8 @@ export class BulkEditPane extends ViewPane { { accessibilityProvider: this._instaService.createInstance(BulkEditAccessibilityProvider), identityProvider: new BulkEditIdentityProvider(), - expandOnlyOnTwistieClick: true + expandOnlyOnTwistieClick: true, + multipleSelectionSupport: false } ); From 0fde639a9e0a80ba81d2bfc6d13e89ce4dc243a2 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 13 Jan 2020 17:35:53 +0100 Subject: [PATCH 205/843] message polish for #88552 --- src/vs/editor/contrib/rename/renameInputField.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/rename/renameInputField.ts b/src/vs/editor/contrib/rename/renameInputField.ts index f70ed3c15d1..dd036ba5df6 100644 --- a/src/vs/editor/contrib/rename/renameInputField.ts +++ b/src/vs/editor/contrib/rename/renameInputField.ts @@ -81,7 +81,7 @@ export class RenameInputField implements IContentWidget { const updateLabel = () => { const [accept, preview] = this._acceptKeybindings; this._keybindingService.lookupKeybinding(accept); - this._label!.innerText = localize('label', "Press {0} to Rename, {1} to Preview", this._keybindingService.lookupKeybinding(accept)?.getLabel(), this._keybindingService.lookupKeybinding(preview)?.getLabel()); + this._label!.innerText = localize('label', "{0} to Rename, {1} to Preview", this._keybindingService.lookupKeybinding(accept)?.getLabel(), this._keybindingService.lookupKeybinding(preview)?.getLabel()); }; updateLabel(); this._disposables.add(this._keybindingService.onDidUpdateKeybindings(updateLabel)); From 517e4391c2381838274acf428aaa4807c4465fa8 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 13 Jan 2020 16:06:55 +0100 Subject: [PATCH 206/843] readonly semantic highlighting --- .../src/modes/javascriptSemanticTokens.ts | 40 +++++++++++++++---- .../server/src/test/semanticTokens.test.ts | 34 ++++++++++++---- .../common/tokenClassificationRegistry.ts | 1 + 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts index 7e4deafa262..fdf22b3cd7c 100644 --- a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts +++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts @@ -20,9 +20,10 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current if (node.kind === ts.SyntaxKind.Identifier) { const symbol = typeChecker.getSymbolAtLocation(node); if (symbol) { - const decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; - if (decl) { - let typeIdx = tokenFromDeclarationMapping[decl.kind]; + let typeIdx = classifySymbol(symbol); + + if (typeIdx !== undefined) { + let modifierSet = 0; if (node.parent) { const parentTypeIdx = tokenFromDeclarationMapping[node.parent.kind]; @@ -30,16 +31,19 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current modifierSet = TokenModifier.declaration; } } - const modifiers = ts.getCombinedModifierFlags(decl); + const decl = symbol.valueDeclaration; + const modifiers = decl ? ts.getCombinedModifierFlags(decl) : 0; + const nodeFlags = decl ? ts.getCombinedNodeFlags(decl) : 0; if (modifiers & ts.ModifierFlags.Static) { modifierSet |= TokenModifier.static; } if (modifiers & ts.ModifierFlags.Async) { modifierSet |= TokenModifier.async; } - if (typeIdx !== undefined) { - resultTokens.push({ start: currentTextDocument.positionAt(node.getStart()), length: node.getWidth(), typeIdx, modifierSet }); + if ((modifiers & ts.ModifierFlags.Readonly) || (nodeFlags & ts.NodeFlags.Const)) { + modifierSet |= TokenModifier.readonly; } + resultTokens.push({ start: currentTextDocument.positionAt(node.getStart()), length: node.getWidth(), typeIdx, modifierSet }); } } } @@ -55,6 +59,27 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current return resultTokens; } +function classifySymbol(symbol: ts.Symbol) { + + const flags = symbol.getFlags(); + if (flags & ts.SymbolFlags.Class) { + return TokenType.class; + } else if (flags & ts.SymbolFlags.Enum) { + return TokenType.enum; + } else if (flags & ts.SymbolFlags.TypeAlias) { + return TokenType.type; + } else if (flags & ts.SymbolFlags.Type) { + if (flags & ts.SymbolFlags.Interface) { + return TokenType.interface; + } if (flags & ts.SymbolFlags.TypeParameter) { + return TokenType.typeParameter; + } + } + const decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; + return tokenFromDeclarationMapping[decl.kind]; +} + + export function getSemanticTokenLegend() { return { types: tokenTypes, modifiers: tokenModifiers }; @@ -62,7 +87,7 @@ export function getSemanticTokenLegend() { const tokenTypes: string[] = ['class', 'enum', 'interface', 'namespace', 'typeParameter', 'type', 'parameter', 'variable', 'property', 'constant', 'function', 'member']; -const tokenModifiers: string[] = ['declaration', 'static', 'async']; +const tokenModifiers: string[] = ['declaration', 'static', 'async', 'readonly']; const enum TokenType { 'class' = 0, @@ -84,6 +109,7 @@ const enum TokenModifier { 'declaration' = 0x01, 'static' = 0x02, 'async' = 0x04, + 'readonly' = 0x08, } const tokenFromDeclarationMapping: { [name: string]: TokenType } = { diff --git a/extensions/html-language-features/server/src/test/semanticTokens.test.ts b/extensions/html-language-features/server/src/test/semanticTokens.test.ts index 0408b3ccdbf..389fa3496f2 100644 --- a/extensions/html-language-features/server/src/test/semanticTokens.test.ts +++ b/extensions/html-language-features/server/src/test/semanticTokens.test.ts @@ -55,7 +55,7 @@ suite('HTML Semantic Tokens', () => { /*2*/'', + /*8*/'', + /*9*/'', + ]; + assertTokens(input, [ + t(3, 8, 1, 'variable.declaration.readonly'), + t(4, 8, 1, 'class.declaration'), t(4, 28, 1, 'property.declaration.static.readonly'), t(4, 42, 3, 'property.declaration.static'), t(4, 47, 3, 'interface'), + t(6, 8, 1, 'variable.declaration.readonly'), t(6, 12, 1, 'variable.readonly'), t(6, 16, 1, 'class'), t(6, 18, 1, 'property.static.readonly'), t(6, 22, 1, 'class'), t(6, 24, 3, 'property.static'), t(6, 28, 6, 'property.readonly'), ]); }); @@ -154,9 +174,9 @@ suite('HTML Semantic Tokens', () => { /*9*/'', ]; assertTokens(input, [ - t(3, 7, 5, 'type.declaration'), t(3, 15, 3, 'variable') /* to investiagte */, + t(3, 7, 5, 'type.declaration'), t(3, 15, 3, 'interface') /* to investiagte */, t(4, 11, 1, 'function.declaration'), t(4, 13, 1, 'typeParameter.declaration'), t(4, 23, 5, 'type'), t(4, 30, 1, 'parameter.declaration'), t(4, 33, 1, 'typeParameter'), t(4, 47, 1, 'typeParameter'), - t(5, 12, 1, 'typeParameter'), t(5, 29, 3, 'variable'), t(5, 41, 5, 'type'), + t(5, 12, 1, 'typeParameter'), t(5, 29, 3, 'interface'), t(5, 41, 5, 'type'), ]); }); diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index 1f387335ec2..7076d918348 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -400,6 +400,7 @@ function registerDefaultClassifications(): void { tokenClassificationRegistry.registerTokenModifier('deprecated', nls.localize('deprecated', "Style to use for symbols that are deprecated."), undefined); tokenClassificationRegistry.registerTokenModifier('modification', nls.localize('modification', "Style to use for write accesses."), undefined); tokenClassificationRegistry.registerTokenModifier('async', nls.localize('async', "Style to use for symbols that are async."), undefined); + tokenClassificationRegistry.registerTokenModifier('readonly', nls.localize('readonly', "Style to use for symbols that are readonly."), undefined); } export function getTokenClassificationRegistry(): ITokenClassificationRegistry { From ae8fee7ba8c1c303fb3cd0ecdee176ac39975404 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 13 Jan 2020 17:49:58 +0100 Subject: [PATCH 207/843] finish test --- .../server/src/modes/javascriptSemanticTokens.ts | 2 +- .../server/src/test/semanticTokens.test.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts index fdf22b3cd7c..3c7b620ab8c 100644 --- a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts +++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts @@ -40,7 +40,7 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current if (modifiers & ts.ModifierFlags.Async) { modifierSet |= TokenModifier.async; } - if ((modifiers & ts.ModifierFlags.Readonly) || (nodeFlags & ts.NodeFlags.Const)) { + if ((modifiers & ts.ModifierFlags.Readonly) || (nodeFlags & ts.NodeFlags.Const) || (symbol.getFlags() & ts.SymbolFlags.EnumMember)) { modifierSet |= TokenModifier.readonly; } resultTokens.push({ start: currentTextDocument.positionAt(node.getStart()), length: node.getWidth(), typeIdx, modifierSet }); diff --git a/extensions/html-language-features/server/src/test/semanticTokens.test.ts b/extensions/html-language-features/server/src/test/semanticTokens.test.ts index 389fa3496f2..384d1467d0b 100644 --- a/extensions/html-language-features/server/src/test/semanticTokens.test.ts +++ b/extensions/html-language-features/server/src/test/semanticTokens.test.ts @@ -155,6 +155,7 @@ suite('HTML Semantic Tokens', () => { assertTokens(input, [ t(3, 8, 1, 'variable.declaration.readonly'), t(4, 8, 1, 'class.declaration'), t(4, 28, 1, 'property.declaration.static.readonly'), t(4, 42, 3, 'property.declaration.static'), t(4, 47, 3, 'interface'), + t(5, 13, 1, 'enum.declaration'), t(5, 17, 1, 'property.declaration.readonly'), t(5, 24, 1, 'property.declaration.readonly'), t(5, 28, 1, 'property.readonly'), t(6, 8, 1, 'variable.declaration.readonly'), t(6, 12, 1, 'variable.readonly'), t(6, 16, 1, 'class'), t(6, 18, 1, 'property.static.readonly'), t(6, 22, 1, 'class'), t(6, 24, 3, 'property.static'), t(6, 28, 6, 'property.readonly'), ]); }); From 7da3ea70a58706081c56d8fb6f76649511087637 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 13 Jan 2020 09:15:36 -0800 Subject: [PATCH 208/843] has -> had --- src/vs/workbench/contrib/terminal/browser/terminalPanel.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 2a0408ff556..2c685281dc9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -97,13 +97,13 @@ export class TerminalPanel extends Panel { this._register(this.onDidChangeVisibility(visible => { if (visible) { - const hasTerminals = this._terminalService.terminalInstances.length > 0; - if (!hasTerminals) { + const hadTerminals = this._terminalService.terminalInstances.length > 0; + if (!hadTerminals) { this._terminalService.createTerminal(); } this._updateFont(); this._updateTheme(); - if (hasTerminals) { + if (hadTerminals) { this._terminalService.getActiveTab()?.setVisible(visible); } } From 91283c1351bc86f3da1aa42ae3f559ee07acc391 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Mon, 13 Jan 2020 10:15:40 -0800 Subject: [PATCH 209/843] Enhance views service (#87505) Add view-moving functionality to viewsservice which has been renamed to viewdescriptorservice. also includes outline view moving --- .../parts/activitybar/activitybarPart.ts | 10 +- .../browser/parts/panel/panelPart.ts | 8 +- src/vs/workbench/browser/parts/views/views.ts | 283 ++++++++++++------ src/vs/workbench/common/views.ts | 29 ++ .../outline/browser/outline.contribution.ts | 77 ++++- .../quickopen/browser/viewPickerHandler.ts | 5 +- .../test/browser/parts/views/views.test.ts | 30 +- 7 files changed, 328 insertions(+), 114 deletions(-) diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 29ac27a90b6..9cdf544c792 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -26,7 +26,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { URI, UriComponents } from 'vs/base/common/uri'; import { ToggleCompositePinnedAction, ICompositeBarColors, ActivityAction, ICompositeActivity } from 'vs/workbench/browser/parts/compositeBarActions'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; -import { IViewsService, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewDescriptorCollection } from 'vs/workbench/common/views'; +import { IViewDescriptorService, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewDescriptorCollection } from 'vs/workbench/common/views'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types'; @@ -88,7 +88,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { @IThemeService themeService: IThemeService, @IStorageService private readonly storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, - @IViewsService private readonly viewsService: IViewsService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService, @@ -189,7 +189,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (viewletDescriptor) { const viewContainer = this.getViewContainer(viewletDescriptor.id); if (viewContainer?.hideIfEmpty) { - const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); if (viewDescriptors?.activeViewDescriptors.length === 0) { this.hideComposite(viewletDescriptor.id); // Update the composite bar by hiding } @@ -410,7 +410,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.enableCompositeActions(viewlet); const viewContainer = this.getViewContainer(viewlet.id); if (viewContainer?.hideIfEmpty) { - const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); if (viewDescriptors) { this.onDidChangeActiveViews(viewlet, viewDescriptors); this.viewletDisposables.set(viewlet.id, viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(viewlet, viewDescriptors))); @@ -551,7 +551,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (viewlet) { const views: { when: string | undefined }[] = []; if (viewContainer) { - const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); if (viewDescriptors) { for (const { when } of viewDescriptors.allViewDescriptors) { views.push({ when: when ? when.serialize() : undefined }); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 354466a8da0..c381e6021cd 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -33,7 +33,7 @@ import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/con import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewsService, IViewDescriptorCollection } from 'vs/workbench/common/views'; +import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, IViewDescriptorCollection } from 'vs/workbench/common/views'; interface ICachedPanel { id: string; @@ -98,9 +98,9 @@ export class PanelPart extends CompositePart implements IPanelService { @IKeybindingService keybindingService: IKeybindingService, @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IExtensionService private readonly extensionService: IExtensionService, - @IViewsService private readonly viewsService: IViewsService, ) { super( notificationService, @@ -184,7 +184,7 @@ export class PanelPart extends CompositePart implements IPanelService { this.enableCompositeActions(panel); const viewContainer = this.getViewContainer(panel.id); if (viewContainer?.hideIfEmpty) { - const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); if (viewDescriptors) { this.onDidChangeActiveViews(panel, viewDescriptors); this.panelDisposables.set(panel.id, viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(panel, viewDescriptors))); @@ -295,7 +295,7 @@ export class PanelPart extends CompositePart implements IPanelService { if (panelDescriptor) { const viewContainer = this.getViewContainer(panelDescriptor.id); if (viewContainer?.hideIfEmpty) { - const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); if (viewDescriptors?.activeViewDescriptors.length === 0) { this.hideComposite(panelDescriptor.id); // Update the composite bar by hiding } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index a3b9148647a..91ca9c36bd0 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/views'; import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IViewsService, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, IViewDescriptorCollection, IViewsRegistry } from 'vs/workbench/common/views'; +import { IViewDescriptorService, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, IViewDescriptorCollection, IViewsRegistry, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -21,6 +21,8 @@ import { values } from 'vs/base/common/map'; import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { toggleClass, addClass } from 'vs/base/browser/dom'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IPaneComposite } from 'vs/workbench/common/panecomposite'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; function filterViewRegisterEvent(container: ViewContainer, event: Event<{ viewContainer: ViewContainer, views: IViewDescriptor[]; }>): Event { return Event.chain(event) @@ -93,14 +95,14 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl constructor( container: ViewContainer, - @IContextKeyService private readonly contextKeyService: IContextKeyService + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService ) { super(); - const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); - const onRelevantViewsRegistered = filterViewRegisterEvent(container, viewsRegistry.onViewsRegistered); + const onRelevantViewsRegistered = filterViewRegisterEvent(container, this.viewDescriptorService.onViewsRegistered); this._register(onRelevantViewsRegistered(this.onViewsRegistered, this)); - const onRelevantViewsMoved = filterViewMoveEvent(container, viewsRegistry.onDidChangeContainer); + const onRelevantViewsMoved = filterViewMoveEvent(container, this.viewDescriptorService.onDidChangeContainer); this._register(onRelevantViewsMoved(({ added, removed }) => { if (isNonEmptyArray(added)) { this.onViewsRegistered(added); @@ -110,13 +112,14 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl } })); - const onRelevantViewsDeregistered = filterViewRegisterEvent(container, viewsRegistry.onViewsDeregistered); + const onRelevantViewsDeregistered = filterViewRegisterEvent(container, this.viewDescriptorService.onViewsDeregistered); this._register(onRelevantViewsDeregistered(this.onViewsDeregistered, this)); const onRelevantContextChange = Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys)); this._register(onRelevantContextChange(this.onContextChanged, this)); - this.onViewsRegistered(viewsRegistry.getViews(container)); + + this.onViewsRegistered(this.viewDescriptorService.getViews(container)); } private onViewsRegistered(viewDescriptors: IViewDescriptor[]): void { @@ -249,7 +252,7 @@ export class ContributableViewsModel extends Disposable { constructor( container: ViewContainer, - viewsService: IViewsService, + viewsService: IViewDescriptorService, protected viewStates = new Map(), ) { super(); @@ -487,13 +490,13 @@ export class PersistentContributableViewsModel extends ContributableViewsModel { constructor( container: ViewContainer, viewletStateStorageId: string, - @IViewsService viewsService: IViewsService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IStorageService storageService: IStorageService, ) { const globalViewsStateStorageId = `${viewletStateStorageId}.hidden`; const viewStates = PersistentContributableViewsModel.loadViewsStates(viewletStateStorageId, globalViewsStateStorageId, storageService); - super(container, viewsService, viewStates); + super(container, viewDescriptorService, viewStates); this.workspaceViewsStateStorageId = viewletStateStorageId; this.globalViewsStateStorageId = globalViewsStateStorageId; @@ -624,105 +627,45 @@ export class PersistentContributableViewsModel extends ContributableViewsModel { } } -export class ViewsService extends Disposable implements IViewsService { +export class ViewOpenerService extends Disposable implements IViewsService { _serviceBrand: undefined; - private readonly viewDescriptorCollections: Map; + private readonly viewContainersRegistry: IViewContainersRegistry; private readonly viewDisposable: Map; - private readonly activeViewContextKeys: Map>; constructor( - @IViewletService private readonly viewletService: IViewletService, - @IContextKeyService private readonly contextKeyService: IContextKeyService + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @IPanelService private readonly panelService: IPanelService, + @IViewletService private readonly viewletService: IViewletService ) { super(); - this.viewDescriptorCollections = new Map(); + this.viewContainersRegistry = Registry.as(ViewExtensions.ViewContainersRegistry); this.viewDisposable = new Map(); - this.activeViewContextKeys = new Map>(); - const viewContainersRegistry = Registry.as(ViewExtensions.ViewContainersRegistry); - const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); - viewContainersRegistry.all.forEach(viewContainer => { - this.onDidRegisterViews(viewContainer, viewsRegistry.getViews(viewContainer)); - this.onDidRegisterViewContainer(viewContainer); - }); - this._register(viewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(viewContainer, views))); - this._register(viewsRegistry.onViewsDeregistered(({ views }) => this.onDidDeregisterViews(views))); - this._register(viewsRegistry.onDidChangeContainer(({ views, to }) => { this.onDidDeregisterViews(views); this.onDidRegisterViews(to, views); })); this._register(toDisposable(() => { this.viewDisposable.forEach(disposable => disposable.dispose()); this.viewDisposable.clear(); })); - this._register(viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onDidRegisterViewContainer(viewContainer))); - this._register(viewContainersRegistry.onDidDeregister(({ viewContainer }) => this.onDidDeregisterViewContainer(viewContainer))); - this._register(toDisposable(() => { - this.viewDescriptorCollections.forEach(({ disposable }) => disposable.dispose()); - this.viewDescriptorCollections.clear(); - })); + + this._register(this.viewDescriptorService.onViewsRegistered(({ views, viewContainer }) => this.onViewsRegistered(views, viewContainer))); + this._register(this.viewDescriptorService.onViewsDeregistered(({ views, viewContainer }) => this.onViewsDeregistered(views, viewContainer))); } - getViewDescriptors(container: ViewContainer): IViewDescriptorCollection | null { - const registeredViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).get(container.id); - if (registeredViewContainer) { - let viewDescriptorCollectionItem = this.viewDescriptorCollections.get(registeredViewContainer); - if (!viewDescriptorCollectionItem) { - // Create and register the collection if does not exist - this.onDidRegisterViewContainer(registeredViewContainer); - viewDescriptorCollectionItem = this.viewDescriptorCollections.get(registeredViewContainer); - } - return viewDescriptorCollectionItem!.viewDescriptorCollection; - } - return null; - } - - async openView(id: string, focus: boolean): Promise { - const viewContainer = Registry.as(ViewExtensions.ViewsRegistry).getViewContainer(id); - if (viewContainer) { - const viewletDescriptor = this.viewletService.getViewlet(viewContainer.id); - if (viewletDescriptor) { - const viewlet = await this.viewletService.openViewlet(viewletDescriptor.id, focus) as IViewsViewlet | null; - if (viewlet && viewlet.openView) { - return viewlet.openView(id, focus); - } - } + private onViewsRegistered(views: IViewDescriptor[], container: ViewContainer): void { + const location = this.viewContainersRegistry.getViewContainerLocation(container); + if (location === undefined) { + return; } - return null; - } - - private onDidRegisterViewContainer(viewContainer: ViewContainer): void { - const disposables = new DisposableStore(); - const viewDescriptorCollection = disposables.add(new ViewDescriptorCollection(viewContainer, this.contextKeyService)); - - this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] }); - viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed), this, disposables); - - this.viewDescriptorCollections.set(viewContainer, { viewDescriptorCollection, disposable: disposables }); - } - - private onDidDeregisterViewContainer(viewContainer: ViewContainer): void { - const viewDescriptorCollectionItem = this.viewDescriptorCollections.get(viewContainer); - if (viewDescriptorCollectionItem) { - viewDescriptorCollectionItem.disposable.dispose(); - this.viewDescriptorCollections.delete(viewContainer); - } - } - - private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[]; }): void { - added.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(true)); - removed.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(false)); - } - - private onDidRegisterViews(container: ViewContainer, views: IViewDescriptor[]): void { - const viewlet = this.viewletService.getViewlet(container.id); + const composite = this.getComposite(container.id, location); for (const viewDescriptor of views) { const disposables = new DisposableStore(); const command: ICommandAction = { id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`, title: { original: `Focus on ${viewDescriptor.name} View`, value: localize('focus view', "Focus on {0} View", viewDescriptor.name) }, - category: viewlet ? viewlet.name : localize('view category', "View"), + category: composite ? composite.name : localize('view category', "View"), }; const when = ContextKeyExpr.has(`${viewDescriptor.id}.active`); @@ -750,7 +693,7 @@ export class ViewsService extends Disposable implements IViewsService { } } - private onDidDeregisterViews(views: IViewDescriptor[]): void { + private onViewsDeregistered(views: IViewDescriptor[], container: ViewContainer): void { for (const view of views) { const disposable = this.viewDisposable.get(view); if (disposable) { @@ -760,6 +703,171 @@ export class ViewsService extends Disposable implements IViewsService { } } + + private async openComposite(compositeId: string, location: ViewContainerLocation, focus?: boolean): Promise { + if (location === ViewContainerLocation.Sidebar) { + return this.viewletService.openViewlet(compositeId, focus); + } else if (location === ViewContainerLocation.Panel) { + return this.panelService.openPanel(compositeId, focus) as IPaneComposite; + } + return undefined; + } + + private getComposite(compositeId: string, location: ViewContainerLocation): { id: string, name: string } | undefined { + if (location === ViewContainerLocation.Sidebar) { + return this.viewletService.getViewlet(compositeId); + } else if (location === ViewContainerLocation.Panel) { + return this.panelService.getPanel(compositeId); + } + + return undefined; + } + + async openView(id: string, focus: boolean): Promise { + const viewContainer = this.viewDescriptorService.getViewContainer(id); + if (viewContainer) { + const location = this.viewContainersRegistry.getViewContainerLocation(viewContainer); + const compositeDescriptor = this.getComposite(viewContainer.id, location!); + if (compositeDescriptor) { + const paneComposite = await this.openComposite(compositeDescriptor.id, location!, focus) as IPaneComposite | undefined; + if (paneComposite && paneComposite.openView) { + return paneComposite.openView(id, focus); + } + } + } + + return null; + } +} + +export class ViewDescriptorService extends Disposable implements IViewDescriptorService { + + _serviceBrand: undefined; + + private readonly _onViewsRegistered: Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }>()); + readonly onViewsRegistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._onViewsRegistered.event; + + private readonly _onViewsDeregistered: Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }>()); + readonly onViewsDeregistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._onViewsDeregistered.event; + + private readonly _onDidChangeContainer: Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>()); + readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._onDidChangeContainer.event; + + private readonly viewDescriptorCollections: Map; + private readonly viewContainers: Map; + private readonly activeViewContextKeys: Map>; + + private readonly viewsRegistry: IViewsRegistry; + private readonly viewContainersRegistry: IViewContainersRegistry; + + constructor( + @IContextKeyService private readonly contextKeyService: IContextKeyService + ) { + super(); + + this.viewDescriptorCollections = new Map(); + this.viewContainers = new Map(); + this.activeViewContextKeys = new Map>(); + + this.viewContainersRegistry = Registry.as(ViewExtensions.ViewContainersRegistry); + this.viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); + this.viewContainersRegistry.all.forEach(viewContainer => { + this.onDidRegisterViews(viewContainer, this.viewsRegistry.getViews(viewContainer)); + this.onDidRegisterViewContainer(viewContainer); + }); + this._register(this.viewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(viewContainer, views))); + this._register(this.viewsRegistry.onViewsDeregistered(({ views, viewContainer }) => this.onDidDeregisterViews(viewContainer, views))); + this._register(this.viewsRegistry.onDidChangeContainer(({ views, from, to }) => { this.onDidDeregisterViews(from, views); this.onDidRegisterViews(to, views); this._onDidChangeContainer.fire({ views, from, to }); })); + + this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onDidRegisterViewContainer(viewContainer))); + this._register(this.viewContainersRegistry.onDidDeregister(({ viewContainer }) => this.onDidDeregisterViewContainer(viewContainer))); + this._register(toDisposable(() => { + this.viewDescriptorCollections.forEach(({ disposable }) => disposable.dispose()); + this.viewDescriptorCollections.clear(); + })); + } + + getViewContainer(viewId: string): ViewContainer | null { + return this.viewContainers.get(viewId) ?? null; + } + + getViewDescriptors(container: ViewContainer): IViewDescriptorCollection | null { + const registeredViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).get(container.id); + if (registeredViewContainer) { + let viewDescriptorCollectionItem = this.viewDescriptorCollections.get(registeredViewContainer); + if (!viewDescriptorCollectionItem) { + // Create and register the collection if does not exist + this.onDidRegisterViewContainer(registeredViewContainer); + viewDescriptorCollectionItem = this.viewDescriptorCollections.get(registeredViewContainer); + } + return viewDescriptorCollectionItem!.viewDescriptorCollection; + } + return null; + } + + moveViews(views: IViewDescriptor[], viewContainer: ViewContainer): void { + if (!views.length) { + return; + } + + const from = this.viewContainers.get(views[0].id); + const to = viewContainer; + + if (from && to && from !== to) { + this.onDidDeregisterViews(from, views); + this.onDidRegisterViews(viewContainer, views); + this._onDidChangeContainer.fire({ views, from, to }); + } + } + + getViews(container: ViewContainer): IViewDescriptor[] { + return this.viewsRegistry.getViews(container); + } + + private onDidRegisterViewContainer(viewContainer: ViewContainer): void { + const disposables = new DisposableStore(); + const viewDescriptorCollection = disposables.add(new ViewDescriptorCollection(viewContainer, this.contextKeyService, this)); + + this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] }); + viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed), this, disposables); + + this.viewDescriptorCollections.set(viewContainer, { viewDescriptorCollection, disposable: disposables }); + } + + private onDidDeregisterViewContainer(viewContainer: ViewContainer): void { + const viewDescriptorCollectionItem = this.viewDescriptorCollections.get(viewContainer); + if (viewDescriptorCollectionItem) { + viewDescriptorCollectionItem.disposable.dispose(); + this.viewDescriptorCollections.delete(viewContainer); + } + } + + private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[]; }): void { + added.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(true)); + removed.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(false)); + } + + private onDidRegisterViews(container: ViewContainer, views: IViewDescriptor[]): void { + const location = this.viewContainersRegistry.getViewContainerLocation(container); + if (location === undefined) { + return; + } + + for (const viewDescriptor of views) { + this.viewContainers.set(viewDescriptor.id, container); + } + + this._onViewsRegistered.fire({ views, viewContainer: container }); + } + + private onDidDeregisterViews(container: ViewContainer, views: IViewDescriptor[]): void { + for (const view of views) { + this.viewContainers.delete(view.id); + } + + this._onViewsDeregistered.fire({ views, viewContainer: container }); + } + private getOrCreateActiveViewContextKey(viewDescriptor: IViewDescriptor): IContextKey { const activeContextKeyId = `${viewDescriptor.id}.active`; let contextKey = this.activeViewContextKeys.get(activeContextKeyId); @@ -784,4 +892,5 @@ export function createFileIconThemableTreeContainerScope(container: HTMLElement, return themeService.onDidFileIconThemeChange(onDidChangeFileIconTheme); } -registerSingleton(IViewsService, ViewsService); +registerSingleton(IViewDescriptorService, ViewDescriptorService); +registerSingleton(IViewsService, ViewOpenerService); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 174df04dff6..72f90b88cd6 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -100,6 +100,11 @@ export interface IViewContainersRegistry { * Returns all view containers in the given location */ getViewContainers(location: ViewContainerLocation): ViewContainer[]; + + /** + * Returns the view container location + */ + getViewContainerLocation(container: ViewContainer): ViewContainerLocation | undefined; } interface ViewOrderDelegate { @@ -157,6 +162,10 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe getViewContainers(location: ViewContainerLocation): ViewContainer[] { return [...(this.viewContainers.get(location) || [])]; } + + getViewContainerLocation(container: ViewContainer): ViewContainerLocation | undefined { + return keys(this.viewContainers).filter(location => this.getViewContainers(location).filter(viewContainer => viewContainer.id === container.id).length > 0)[0]; + } } Registry.add(Extensions.ViewContainersRegistry, new ViewContainersRegistryImpl()); @@ -336,14 +345,34 @@ export interface IViewsViewlet extends IViewlet { } +export const IViewDescriptorService = createDecorator('viewDescriptorService'); export const IViewsService = createDecorator('viewsService'); + export interface IViewsService { _serviceBrand: undefined; openView(id: string, focus?: boolean): Promise; +} + + +export interface IViewDescriptorService { + + _serviceBrand: undefined; + + readonly onViewsRegistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }>; + + readonly onViewsDeregistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }>; + + readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>; + + moveViews(views: IViewDescriptor[], viewContainer: ViewContainer): void; + + getViews(container: ViewContainer): IViewDescriptor[]; getViewDescriptors(container: ViewContainer): IViewDescriptorCollection | null; + + getViewContainer(viewId: string): ViewContainer | null; } // Custom views diff --git a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts index d95ee1360d0..bbc5693905b 100644 --- a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts +++ b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts @@ -4,16 +4,56 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions } from 'vs/workbench/common/views'; +import { IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions, ViewContainer, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService, IViewsService } from 'vs/workbench/common/views'; import { OutlinePane } from './outlinePane'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { OutlineConfigKeys, OutlineViewId } from 'vs/editor/contrib/documentSymbols/outline'; import { VIEW_CONTAINER } from 'vs/workbench/contrib/files/browser/explorerViewlet'; +import { Action } from 'vs/base/common/actions'; +import { IWorkbenchActionRegistry, Extensions as ActionsExtensions } from 'vs/workbench/common/actions'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; // import './outlineNavigation'; +export const PANEL_ID = 'panel.view.outline'; + +export class OutlineViewPaneContainer extends ViewPaneContainer { + constructor( + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, + @ITelemetryService telemetryService: ITelemetryService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IStorageService protected storageService: IStorageService, + @IConfigurationService configurationService: IConfigurationService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService, + ) { + super(PANEL_ID, `${PANEL_ID}.state`, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService); + } +} + +export const VIEW_CONTAINER_PANEL: ViewContainer = + Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ + id: PANEL_ID, + ctorDescriptor: new SyncDescriptor(OutlineViewPaneContainer), + name: localize('name', "Outline"), + hideIfEmpty: true + }, ViewContainerLocation.Panel); + + const _outlineDesc = { id: OutlineViewId, name: localize('name', "Outline"), @@ -28,6 +68,41 @@ const _outlineDesc = { Registry.as(ViewExtensions.ViewsRegistry).registerViews([_outlineDesc], VIEW_CONTAINER); +let inPanel = false; + +export class ToggleOutlinePositionAction extends Action { + + static ID = 'outline.view.togglePosition'; + static LABEL = 'Toggle Outline View Position'; + + constructor( + id: string, + label: string, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @IViewsService private readonly viewsService: IViewsService + ) { + super(id, label, '', true); + } + + async run(): Promise { + if (!inPanel) { + this.viewDescriptorService.moveViews([_outlineDesc], VIEW_CONTAINER_PANEL); + this.viewsService.openView(OutlineViewId, true); + inPanel = true; + } else { + this.viewDescriptorService.moveViews([_outlineDesc], VIEW_CONTAINER); + this.viewsService.openView(OutlineViewId, true); + + inPanel = false; + } + + } +} + +Registry.as(ActionsExtensions.WorkbenchActions) + .registerWorkbenchAction(SyncActionDescriptor.create(ToggleOutlinePositionAction, ToggleOutlinePositionAction.ID, ToggleOutlinePositionAction.LABEL), 'Show Release Notes'); + + Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ 'id': 'outline', 'order': 117, diff --git a/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts b/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts index 587dc075b24..eefa4456728 100644 --- a/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts @@ -16,7 +16,7 @@ import { Action } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { fuzzyContains, stripWildcards } from 'vs/base/common/strings'; import { matchesFuzzy } from 'vs/base/common/filters'; -import { IViewsRegistry, ViewContainer, IViewsService, IViewContainersRegistry, Extensions as ViewExtensions } from 'vs/workbench/common/views'; +import { IViewsRegistry, ViewContainer, IViewDescriptorService, IViewContainersRegistry, Extensions as ViewExtensions, IViewsService } from 'vs/workbench/common/views'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -70,6 +70,7 @@ export class ViewPickerHandler extends QuickOpenHandler { constructor( @IViewletService private readonly viewletService: IViewletService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IViewsService private readonly viewsService: IViewsService, @IOutputService private readonly outputService: IOutputService, @ITerminalService private readonly terminalService: ITerminalService, @@ -197,7 +198,7 @@ export class ViewPickerHandler extends QuickOpenHandler { private hasToShowViewlet(viewlet: ViewletDescriptor): boolean { const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).get(viewlet.id); if (viewContainer?.hideIfEmpty) { - const viewsCollection = this.viewsService.getViewDescriptors(viewContainer); + const viewsCollection = this.viewDescriptorService.getViewDescriptors(viewContainer); return !!viewsCollection && viewsCollection.activeViewDescriptors.length > 0; } return true; diff --git a/src/vs/workbench/test/browser/parts/views/views.test.ts b/src/vs/workbench/test/browser/parts/views/views.test.ts index a5244e4de17..49502d7bec9 100644 --- a/src/vs/workbench/test/browser/parts/views/views.test.ts +++ b/src/vs/workbench/test/browser/parts/views/views.test.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ContributableViewsModel, ViewsService, IViewState } from 'vs/workbench/browser/parts/views/views'; -import { IViewsRegistry, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views'; +import { ContributableViewsModel, ViewDescriptorService, IViewState } from 'vs/workbench/browser/parts/views/views'; +import { IViewsRegistry, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { move } from 'vs/base/common/arrays'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -38,14 +38,14 @@ class ViewDescriptorSequence { suite('ContributableViewsModel', () => { - let viewsService: IViewsService; + let viewDescriptorService: IViewDescriptorService; let contextKeyService: IContextKeyService; setup(() => { const instantiationService: TestInstantiationService = workbenchInstantiationService(); contextKeyService = instantiationService.createInstance(ContextKeyService); instantiationService.stub(IContextKeyService, contextKeyService); - viewsService = instantiationService.createInstance(ViewsService); + viewDescriptorService = instantiationService.createInstance(ViewDescriptorService); }); teardown(() => { @@ -53,12 +53,12 @@ suite('ContributableViewsModel', () => { }); test('empty model', function () { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); assert.equal(model.visibleViewDescriptors.length, 0); }); test('register/unregister', () => { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); assert.equal(model.visibleViewDescriptors.length, 0); @@ -84,7 +84,7 @@ suite('ContributableViewsModel', () => { }); test('when contexts', async function () { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); assert.equal(model.visibleViewDescriptors.length, 0); @@ -128,7 +128,7 @@ suite('ContributableViewsModel', () => { }); test('when contexts - multiple', async function () { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1' }; @@ -151,7 +151,7 @@ suite('ContributableViewsModel', () => { }); test('when contexts - multiple 2', async function () { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1', when: ContextKeyExpr.equals('showview1', true) }; @@ -174,7 +174,7 @@ suite('ContributableViewsModel', () => { }); test('setVisible', () => { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1', canToggleVisibility: true }; @@ -219,7 +219,7 @@ suite('ContributableViewsModel', () => { }); test('move', () => { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1' }; @@ -250,7 +250,7 @@ suite('ContributableViewsModel', () => { test('view states', async function () { const viewStates = new Map(); viewStates.set('view1', { visibleGlobal: false, collapsed: false, visibleWorkspace: undefined }); - const model = new ContributableViewsModel(container, viewsService, viewStates); + const model = new ContributableViewsModel(container, viewDescriptorService, viewStates); const seq = new ViewDescriptorSequence(model); assert.equal(model.visibleViewDescriptors.length, 0); @@ -270,7 +270,7 @@ suite('ContributableViewsModel', () => { test('view states and when contexts', async function () { const viewStates = new Map(); viewStates.set('view1', { visibleGlobal: false, collapsed: false, visibleWorkspace: undefined }); - const model = new ContributableViewsModel(container, viewsService, viewStates); + const model = new ContributableViewsModel(container, viewDescriptorService, viewStates); const seq = new ViewDescriptorSequence(model); assert.equal(model.visibleViewDescriptors.length, 0); @@ -300,7 +300,7 @@ suite('ContributableViewsModel', () => { test('view states and when contexts multiple views', async function () { const viewStates = new Map(); viewStates.set('view1', { visibleGlobal: false, collapsed: false, visibleWorkspace: undefined }); - const model = new ContributableViewsModel(container, viewsService, viewStates); + const model = new ContributableViewsModel(container, viewDescriptorService, viewStates); const seq = new ViewDescriptorSequence(model); assert.equal(model.visibleViewDescriptors.length, 0); @@ -344,7 +344,7 @@ suite('ContributableViewsModel', () => { }); test('remove event is not triggered if view was hidden and removed', async function () { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); const viewDescriptor: IViewDescriptor = { From 4e7b4bc3269438d3df80d1dd5944d15eb265f4af Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 13 Jan 2020 10:40:38 -0800 Subject: [PATCH 210/843] missed rename for viewsservice --- src/vs/workbench/browser/parts/views/views.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 91ca9c36bd0..b2911ed6663 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -627,7 +627,7 @@ export class PersistentContributableViewsModel extends ContributableViewsModel { } } -export class ViewOpenerService extends Disposable implements IViewsService { +export class ViewsService extends Disposable implements IViewsService { _serviceBrand: undefined; @@ -893,4 +893,4 @@ export function createFileIconThemableTreeContainerScope(container: HTMLElement, } registerSingleton(IViewDescriptorService, ViewDescriptorService); -registerSingleton(IViewsService, ViewOpenerService); +registerSingleton(IViewsService, ViewsService); From 18f0a5bd2164993acbf12c9eba5a7e826da07f5c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 13 Jan 2020 19:57:17 +0100 Subject: [PATCH 211/843] add reaonly support, update typescript-vscode-sh-plugin version --- extensions/typescript-language-features/package.json | 4 ++-- .../src/features/semanticTokens.ts | 2 ++ extensions/typescript-language-features/yarn.lock | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index c63e6a90901..34b17851a94 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -19,14 +19,14 @@ "jsonc-parser": "^2.1.1", "rimraf": "^2.6.3", "semver": "5.5.1", - "typescript-vscode-sh-plugin": "^0.3.0", + "typescript-vscode-sh-plugin": "^0.4.0", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0" }, "devDependencies": { "@types/node": "^12.11.7", - "@types/semver": "^5.5.0", "@types/rimraf": "2.0.2", + "@types/semver": "^5.5.0", "vscode": "^1.1.10" }, "scripts": { diff --git a/extensions/typescript-language-features/src/features/semanticTokens.ts b/extensions/typescript-language-features/src/features/semanticTokens.ts index f9f489321a2..de3f11e81d7 100644 --- a/extensions/typescript-language-features/src/features/semanticTokens.ts +++ b/extensions/typescript-language-features/src/features/semanticTokens.ts @@ -23,6 +23,7 @@ export function register(selector: vscode.DocumentSelector, client: ITypeScriptS /** * Prototype of a SemanticTokensProvider, relying on the experimental `encodedSemanticClassifications-full` request from the TypeScript server. * As the results retured by the TypeScript server are limited, we also add a Typescript plugin (typescript-vscode-sh-plugin) to enrich the returned token. + * See https://github.com/aeschli/typescript-vscode-sh-plugin. */ class SemanticTokensProvider implements vscode.SemanticTokensProvider { @@ -130,6 +131,7 @@ enum TokenModifier { 'declaration', 'static', 'async', + 'readonly', _sentinel } diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index 53bc5bf315d..d3455708d26 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -626,10 +626,10 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -typescript-vscode-sh-plugin@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.3.0.tgz#a071fa28187259a04e3d6862adba41a6332106ff" - integrity sha512-pqPgYa1L64/2LnhP/tJz/+hUsbKzQRMGCdp6Z5Z/dhAdjChB/0WZvovbwDlApGWxOvNAG+oub9TXnJ1yT0WfXQ== +typescript-vscode-sh-plugin@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.4.0.tgz#1b3661f41970a3003099cb3785f7788b5703b751" + integrity sha512-UzqwDMoo/O5asQmUwaiW29cJG/5YzS0EK/4UyyB6UGLNY65CX9/CTp1RHYBmqdh7wq7M0PDqZ8QS1dOWEYnPuQ== uri-js@^4.2.2: version "4.2.2" From 8d1456e210a67f271924cecd68912a086562b9c7 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 13 Jan 2020 10:58:53 -0800 Subject: [PATCH 212/843] Fix search editor URI malform error --- src/vs/workbench/contrib/search/browser/searchEditorCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts index 6d9e6ee65ee..07177d95ea8 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts @@ -85,7 +85,7 @@ export class SearchEditorInput extends EditorInput { } getResource(): URI { - return URI.from({ scheme: 'untitled', authority: 'search-editor', path: this.config.query, fragment: `${this.instanceNumber}` }); + return URI.from({ scheme: 'search-editor', fragment: `${this.instanceNumber}` }); } getName(): string { From 5e1dbb10964c3606b2b887bb21566ef70a131042 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 13 Jan 2020 11:47:25 -0800 Subject: [PATCH 213/843] fix(debug): don't insert task boundaries between events (#88459) * fix(debug): don't insert task boundaries between events See discussion in https://github.com/microsoft/vscode-js-debug/issues/206. This PR adjusts logic such that we only assert task boundaries around requests and responses, rather than around every single event. I believe this will solve the primary case where misordering can happen, as given in the existing unit test and described more verbosely in the doc comment in this PR. A more conservative approach would be to only omit the boundary between events of the same type. That would be safer, but I browsing through the code I didn't see any cases where it looked like we could get tripped up by bucketing here (e.g. cases where we resolve a deferred promise in an event handler). Seems to fix the performance issue for me. * fixup! add unit tests for ordering --- .../debug/common/abstractDebugAdapter.ts | 102 ++++++++++++------ .../debug/test/common/rawDebugSession.test.ts | 52 +++++++++ 2 files changed, 121 insertions(+), 33 deletions(-) create mode 100644 src/vs/workbench/contrib/debug/test/common/rawDebugSession.test.ts diff --git a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts index f060ba6c665..93a76d52c1e 100644 --- a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts +++ b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts @@ -5,20 +5,19 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IDebugAdapter } from 'vs/workbench/contrib/debug/common/debug'; -import { timeout, Queue } from 'vs/base/common/async'; +import { timeout } from 'vs/base/common/async'; /** * Abstract implementation of the low level API for a debug adapter. * Missing is how this API communicates with the debug adapter. */ export abstract class AbstractDebugAdapter implements IDebugAdapter { - private sequence: number; private pendingRequests = new Map void>(); private requestCallback: ((request: DebugProtocol.Request) => void) | undefined; private eventCallback: ((request: DebugProtocol.Event) => void) | undefined; private messageCallback: ((message: DebugProtocol.ProtocolMessage) => void) | undefined; - private readonly queue = new Queue(); + private queue: DebugProtocol.ProtocolMessage[] = []; protected readonly _onError = new Emitter(); protected readonly _onExit = new Emitter(); @@ -64,8 +63,7 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { sendResponse(response: DebugProtocol.Response): void { if (response.seq > 0) { this._onError.fire(new Error(`attempt to send more than one response for command ${response.command}`)); - } - else { + } else { this.internalSend('response', response); } } @@ -107,35 +105,73 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { acceptMessage(message: DebugProtocol.ProtocolMessage): void { if (this.messageCallback) { this.messageCallback(message); + } else { + this.queue.push(message); + if (this.queue.length === 1) { + // first item = need to start processing loop + this.processQueue(); + } } - else { - this.queue.queue(() => { - switch (message.type) { - case 'event': - if (this.eventCallback) { - this.eventCallback(message); - } - break; - case 'request': - if (this.requestCallback) { - this.requestCallback(message); - } - break; - case 'response': - const response = message; - const clb = this.pendingRequests.get(response.request_seq); - if (clb) { - this.pendingRequests.delete(response.request_seq); - clb(response); - } - break; - } + } - // Artificially queueing protocol messages guarantees that any microtasks for - // previous message finish before next message is processed. This is essential - // to guarantee ordering when using promises anywhere along the call path. - return timeout(0); - }); + /** + * Returns whether we should insert a timeout between processing messageA + * and messageB. Artificially queueing protocol messages guarantees that any + * microtasks for previous message finish before next message is processed. + * This is essential ordering when using promises anywhere along the call path. + * + * For example, take the following, where `chooseAndSendGreeting` returns + * a person name and then emits a greeting event: + * + * ``` + * let person: string; + * adapter.onGreeting(() => console.log('hello', person)); + * person = await adapter.chooseAndSendGreeting(); + * ``` + * + * Because the event is dispatched synchronously, it may fire before person + * is assigned if they're processed in the same task. Inserting a task + * boundary avoids this issue. + */ + protected needsTaskBoundaryBetween(messageA: DebugProtocol.ProtocolMessage, messageB: DebugProtocol.ProtocolMessage) { + return messageA.type !== 'event' || messageB.type !== 'event'; + } + + /** + * Reads and dispatches items from the queue until it is empty. + */ + private async processQueue() { + let message: DebugProtocol.ProtocolMessage | undefined; + while (this.queue.length) { + if (!message || this.needsTaskBoundaryBetween(this.queue[0], message)) { + await timeout(0); + } + + message = this.queue.shift(); + if (!message) { + return; // may have been disposed of + } + + switch (message.type) { + case 'event': + if (this.eventCallback) { + this.eventCallback(message); + } + break; + case 'request': + if (this.requestCallback) { + this.requestCallback(message); + } + break; + case 'response': + const response = message; + const clb = this.pendingRequests.get(response.request_seq); + if (clb) { + this.pendingRequests.delete(response.request_seq); + clb(response); + } + break; + } } } @@ -172,6 +208,6 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { } dispose(): void { - this.queue.dispose(); + this.queue = []; } } diff --git a/src/vs/workbench/contrib/debug/test/common/rawDebugSession.test.ts b/src/vs/workbench/contrib/debug/test/common/rawDebugSession.test.ts new file mode 100644 index 00000000000..16c02f6cb02 --- /dev/null +++ b/src/vs/workbench/contrib/debug/test/common/rawDebugSession.test.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { MockDebugAdapter } from 'vs/workbench/contrib/debug/test/common/mockDebug'; +import { timeout } from 'vs/base/common/async'; + +suite('Debug - AbstractDebugAdapter', () => { + suite('event ordering', () => { + let adapter: MockDebugAdapter; + let output: string[]; + setup(() => { + adapter = new MockDebugAdapter(); + output = []; + adapter.onEvent(ev => { + output.push((ev as DebugProtocol.OutputEvent).body.output); + Promise.resolve().then(() => output.push('--end microtask--')); + }); + }); + + const evaluate = async (expression: string) => { + await new Promise(resolve => adapter.sendRequest('evaluate', { expression }, resolve)); + output.push(`=${expression}`); + Promise.resolve().then(() => output.push('--end microtask--')); + }; + + test('inserts task boundary before response', async () => { + await evaluate('before.foo'); + await timeout(0); + + assert.deepStrictEqual(output, ['before.foo', '--end microtask--', '=before.foo', '--end microtask--']); + }); + + test('inserts task boundary after response', async () => { + await evaluate('after.foo'); + await timeout(0); + + assert.deepStrictEqual(output, ['=after.foo', '--end microtask--', 'after.foo', '--end microtask--']); + }); + + test('does not insert boundaries between events', async () => { + adapter.sendEventBody('output', { output: 'a' }); + adapter.sendEventBody('output', { output: 'b' }); + adapter.sendEventBody('output', { output: 'c' }); + await timeout(0); + + assert.deepStrictEqual(output, ['a', 'b', 'c', '--end microtask--', '--end microtask--', '--end microtask--']); + }); + }); +}); From a5f90a56850809fc9e1a55a351c147597e102238 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 13 Jan 2020 21:06:57 +0100 Subject: [PATCH 214/843] polish html sem highlighting --- .../server/src/modes/javascriptSemanticTokens.ts | 4 ++-- .../server/src/test/semanticTokens.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts index 3c7b620ab8c..38a87159a32 100644 --- a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts +++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts @@ -17,7 +17,7 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current const typeChecker = program.getTypeChecker(); function visit(node: ts.Node) { - if (node.kind === ts.SyntaxKind.Identifier) { + if (ts.isIdentifier(node)) { const symbol = typeChecker.getSymbolAtLocation(node); if (symbol) { let typeIdx = classifySymbol(symbol); @@ -76,7 +76,7 @@ function classifySymbol(symbol: ts.Symbol) { } } const decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; - return tokenFromDeclarationMapping[decl.kind]; + return decl && tokenFromDeclarationMapping[decl.kind]; } diff --git a/extensions/html-language-features/server/src/test/semanticTokens.test.ts b/extensions/html-language-features/server/src/test/semanticTokens.test.ts index 384d1467d0b..ea3777e84e9 100644 --- a/extensions/html-language-features/server/src/test/semanticTokens.test.ts +++ b/extensions/html-language-features/server/src/test/semanticTokens.test.ts @@ -147,7 +147,7 @@ suite('HTML Semantic Tokens', () => { /*3*/' const f = 9;', /*4*/' class A { static readonly t = 9; static url: URL; }', /*5*/' const enum E { A = 9, B = A + 1 }', - /*6*/' const x = f + A.t + A.url.origin;', + /*6*/' console.log(f + A.t + A.url.origin);', /*7*/'', /*8*/'', /*9*/'', @@ -156,7 +156,7 @@ suite('HTML Semantic Tokens', () => { t(3, 8, 1, 'variable.declaration.readonly'), t(4, 8, 1, 'class.declaration'), t(4, 28, 1, 'property.declaration.static.readonly'), t(4, 42, 3, 'property.declaration.static'), t(4, 47, 3, 'interface'), t(5, 13, 1, 'enum.declaration'), t(5, 17, 1, 'property.declaration.readonly'), t(5, 24, 1, 'property.declaration.readonly'), t(5, 28, 1, 'property.readonly'), - t(6, 8, 1, 'variable.declaration.readonly'), t(6, 12, 1, 'variable.readonly'), t(6, 16, 1, 'class'), t(6, 18, 1, 'property.static.readonly'), t(6, 22, 1, 'class'), t(6, 24, 3, 'property.static'), t(6, 28, 6, 'property.readonly'), + t(6, 2, 7, 'variable'), t(6, 10, 3, 'member'), t(6, 14, 1, 'variable.readonly'), t(6, 18, 1, 'class'), t(6, 20, 1, 'property.static.readonly'), t(6, 24, 1, 'class'), t(6, 26, 3, 'property.static'), t(6, 30, 6, 'property.readonly'), ]); }); From fc57a1421df4ec323e36d1b8c29789300998fe80 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 13 Jan 2020 21:09:37 +0100 Subject: [PATCH 215/843] update typescript-language-features --- extensions/typescript-language-features/package.json | 2 +- extensions/typescript-language-features/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 34b17851a94..3ff2f98b8e4 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -19,7 +19,7 @@ "jsonc-parser": "^2.1.1", "rimraf": "^2.6.3", "semver": "5.5.1", - "typescript-vscode-sh-plugin": "^0.4.0", + "typescript-vscode-sh-plugin": "^0.4.1", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0" }, diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index d3455708d26..98feebef484 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -626,10 +626,10 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -typescript-vscode-sh-plugin@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.4.0.tgz#1b3661f41970a3003099cb3785f7788b5703b751" - integrity sha512-UzqwDMoo/O5asQmUwaiW29cJG/5YzS0EK/4UyyB6UGLNY65CX9/CTp1RHYBmqdh7wq7M0PDqZ8QS1dOWEYnPuQ== +typescript-vscode-sh-plugin@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.4.1.tgz#6982958419ca760c0add761dd1d5f398815bee8c" + integrity sha512-IPsz/FNuk9HsjaEOsJJxGkiKGK5E4muZtjntp/BDWYZVYOj3R8JtX6++pQ0ftMOcZwxjKxeBmnS1FSUdVMifSw== uri-js@^4.2.2: version "4.2.2" From fbb4fbcd4ba862cade0bbf39632a8b7a88aadef9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 13 Jan 2020 21:06:05 +0100 Subject: [PATCH 216/843] fix issue with accessibility provider, #88554 --- .../contrib/bulkEdit/browser/bulkEditTree.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 188c0569442..bbd92f7b31a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -118,38 +118,44 @@ export class BulkEditAccessibilityProvider implements IAccessibilityProvider 0) { if (element.edit.type & BulkFileOperationType.Rename && element.edit.newUri) { return localize( - 'area.renameAndEdit', "Renaming {0} to {1}, also making text edits", + 'aria.renameAndEdit', "Renaming {0} to {1}, also making text edits", this._labelService.getUriLabel(element.edit.uri, { relative: true }), this._labelService.getUriLabel(element.edit.newUri, { relative: true }) ); } else if (element.edit.type & BulkFileOperationType.Create) { return localize( - 'area.createAndEdit', "Creating {0}, also making text edits", + 'aria.createAndEdit', "Creating {0}, also making text edits", this._labelService.getUriLabel(element.edit.uri, { relative: true }) ); } else if (element.edit.type & BulkFileOperationType.Delete) { return localize( - 'area.deleteAndEdit', "Deleting {0}, also making text edits", + 'aria.deleteAndEdit', "Deleting {0}, also making text edits", + this._labelService.getUriLabel(element.edit.uri, { relative: true }), + ); + } else { + return localize( + 'aria.editOnly', "{0}, making text edits", this._labelService.getUriLabel(element.edit.uri, { relative: true }), ); } + } else { if (element.edit.type & BulkFileOperationType.Rename && element.edit.newUri) { return localize( - 'area.rename', "Renaming {0} to {1}", + 'aria.rename', "Renaming {0} to {1}", this._labelService.getUriLabel(element.edit.uri, { relative: true }), this._labelService.getUriLabel(element.edit.newUri, { relative: true }) ); } else if (element.edit.type & BulkFileOperationType.Create) { return localize( - 'area.create', "Creating {0}", + 'aria.create', "Creating {0}", this._labelService.getUriLabel(element.edit.uri, { relative: true }) ); } else if (element.edit.type & BulkFileOperationType.Delete) { return localize( - 'area.delete', "Deleting {0}", + 'aria.delete', "Deleting {0}", this._labelService.getUriLabel(element.edit.uri, { relative: true }), ); } @@ -159,7 +165,7 @@ export class BulkEditAccessibilityProvider implements IAccessibilityProvider 0 && element.inserting.length > 0) { // edit: replace - return localize('aria.replace', "line {0}, replacing {1} with {0}", element.edit.edit.range.startLineNumber, element.selecting, element.inserting); + return localize('aria.replace', "line {0}, replacing {1} with {2}", element.edit.edit.range.startLineNumber, element.selecting, element.inserting); } else if (element.selecting.length > 0 && element.inserting.length === 0) { // edit: delete return localize('aria.del', "line {0}, removing {1}", element.edit.edit.range.startLineNumber, element.selecting); From 0bf6738abe5d7009c5d1ab4ef1a3d10c792e05b1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 13 Jan 2020 21:17:21 +0100 Subject: [PATCH 217/843] add AriaProvider, #88553 --- .../contrib/bulkEdit/browser/bulkEditPane.ts | 3 ++- .../contrib/bulkEdit/browser/bulkEditTree.ts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 13e60a3218a..d0310fcc727 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -6,7 +6,7 @@ import 'vs/css!./bulkEdit'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; -import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; +import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider, BulkEditAriaProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; @@ -97,6 +97,7 @@ export class BulkEditPane extends ViewPane { this._instaService.createInstance(BulkEditDataSource), { accessibilityProvider: this._instaService.createInstance(BulkEditAccessibilityProvider), + ariaProvider: new BulkEditAriaProvider(), identityProvider: new BulkEditIdentityProvider(), expandOnlyOnTwistieClick: true, multipleSelectionSupport: false diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index bbd92f7b31a..2488030bcfa 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -20,6 +20,7 @@ import { FileKind } from 'vs/platform/files/common/files'; import { localize } from 'vs/nls'; import { ILabelService } from 'vs/platform/label/common/label'; import type { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import type { IAriaProvider } from 'vs/base/browser/ui/list/listView'; // --- VIEW MODEL @@ -192,6 +193,21 @@ export class BulkEditIdentityProvider implements IIdentityProvider { + + getSetSize(_element: BulkEditElement, _index: number, listLength: number): number { + return listLength; + } + + getPosInSet(_element: BulkEditElement, index: number): number { + return index; + } + + getRole?(_element: BulkEditElement): string { + return 'checkbox'; + } +} + // --- RENDERER class FileElementTemplate { From 27546bcb564e0f7246bd9fa47887c0883c5757e4 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 13 Jan 2020 21:35:22 +0100 Subject: [PATCH 218/843] fix test failure --- src/vs/editor/common/services/modelServiceImpl.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 9e0c8349ade..1d60306a4cc 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -456,7 +456,8 @@ class SemanticColoringFeature extends Disposable { this._semanticStyling = this._register(new SemanticStyling(themeService, logService)); const isSemanticColoringEnabled = (model: ITextModel) => { - return configurationService.getValue(SemanticColoringFeature.SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri }).enabled; + const options = configurationService.getValue(SemanticColoringFeature.SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri }); + return options && options.enabled; }; const register = (model: ITextModel) => { this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling); From 137c78d711a6d701a928c29241ac50f89000e41a Mon Sep 17 00:00:00 2001 From: Konstantin Solomatov Date: Tue, 7 Jan 2020 12:26:06 -0800 Subject: [PATCH 219/843] Code cleanup --- .../contrib/terminal/browser/terminalPanel.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 0cec6b4b0c6..cfa67e91d8f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -97,19 +97,14 @@ export class TerminalPanel extends Panel { this._register(this.onDidChangeVisibility(visible => { if (visible) { - if (this._terminalService.terminalInstances.length > 0) { - this._updateFont(); - this._updateTheme(); + const hasTerminals = this._terminalService.terminalInstances.length > 0; + if (!hasTerminals) { + this._terminalService.createTerminal(); + } + this._updateFont(); + this._updateTheme(); + if (!hasTerminals) { this._terminalService.getActiveTab()?.setVisible(visible); - } else { - // Check if instances were already restored as part of workbench restore - if (this._terminalService.terminalInstances.length === 0) { - this._terminalService.createTerminal(); - } - if (this._terminalService.terminalInstances.length > 0) { - this._updateFont(); - this._updateTheme(); - } } } })); From fb9a7f0398045d869d9ad3a87559875dca9cab40 Mon Sep 17 00:00:00 2001 From: Konstantin Solomatov Date: Tue, 7 Jan 2020 13:03:11 -0800 Subject: [PATCH 220/843] Fix incorrect negation --- src/vs/workbench/contrib/terminal/browser/terminalPanel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index cfa67e91d8f..2a0408ff556 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -103,7 +103,7 @@ export class TerminalPanel extends Panel { } this._updateFont(); this._updateTheme(); - if (!hasTerminals) { + if (hasTerminals) { this._terminalService.getActiveTab()?.setVisible(visible); } } From 115dde7d6529b7b214805e47c2cc0e263b57342d Mon Sep 17 00:00:00 2001 From: kieferrm Date: Thu, 9 Jan 2020 20:04:07 -0800 Subject: [PATCH 221/843] issue flow configuration --- .github/feature-requests.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/feature-requests.yml diff --git a/.github/feature-requests.yml b/.github/feature-requests.yml new file mode 100644 index 00000000000..18055b84486 --- /dev/null +++ b/.github/feature-requests.yml @@ -0,0 +1,34 @@ +{ + typeLabel: { + name: 'feature-request' + }, + candidateMilestone: { + number: 107, + name: 'Backlog Candidates' + }, + approvedMilestone: { + number: 8, + name: 'Backlog' + }, + onLabeled: { + delay: 60, + perform: true + }, + onCandidateMilestoned: { + candidatesComment: "This feature request is now a candidate for our backlog. The community has 60 days to upvote the issue. If it receives 20 upvotes we will move it to our backlog. If not, we will close it. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!", + perform: true + }, + onMonitorUpvotes: { + upvoteThreshold: 20, + acceptanceComment: ":slightly_smiling_face: This feature request received a sufficient number of community upvotes and we moved it to our backlog. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!", + perform: true + }, + onMonitorDaysOnCandidateMilestone: { + daysOnMilestone: 60, + warningPeriod: 10, + numberOfCommentsToPreventAutomaticRejection: 20, + rejectionComment: ":slightly_frowning_face: In the last 60 days, this feature request has received less than 20 community upvotes and we closed it. Still a big Thank You to you for taking the time to create this issue! To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!", + warningComment: "This feature request has not yet received the 20 community upvotes it takes to make to our backlog. 10 days to go. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding", + perform: true + } +} From fe572bbb59003c036f4708bf45b82abf3d6b692c Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Sun, 12 Jan 2020 02:39:01 -0500 Subject: [PATCH 222/843] Fixes #88242 --- .../contrib/debug/browser/baseDebugView.ts | 121 +++++++++--------- .../debug/browser/watchExpressionsView.ts | 11 +- 2 files changed, 74 insertions(+), 58 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index c40e105ffef..160c2c8f3cd 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -9,7 +9,7 @@ import { Expression, Variable, ExpressionContainer } from 'vs/workbench/contrib/ import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInputValidationOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -18,6 +18,7 @@ import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabe import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { ReplEvaluationResult } from 'vs/workbench/contrib/debug/common/replModel'; +import { once } from 'vs/base/common/functional'; export const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; export const twistiePixels = 20; @@ -137,8 +138,7 @@ export interface IExpressionTemplateData { name: HTMLSpanElement; value: HTMLSpanElement; inputBoxContainer: HTMLElement; - enableInputBox(options: IInputBoxOptions): void; - toDispose: IDisposable[]; + toDispose: IDisposable; label: HighlightedLabel; } @@ -159,77 +159,84 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer { - name.style.display = 'none'; - value.style.display = 'none'; - inputBoxContainer.style.display = 'initial'; - - const inputBox = new InputBox(inputBoxContainer, this.contextViewService, options); - const styler = attachInputBoxStyler(inputBox, this.themeService); - - inputBox.value = replaceWhitespace(options.initialValue); - inputBox.focus(); - inputBox.select(); - - let disposed = false; - toDispose.push(inputBox); - toDispose.push(styler); - - const wrapUp = (renamed: boolean) => { - if (!disposed) { - disposed = true; - this.debugService.getViewModel().setSelectedExpression(undefined); - options.onFinish(inputBox.value, renamed); - - // need to remove the input box since this template will be reused. - inputBoxContainer.removeChild(inputBox.element); - name.style.display = 'initial'; - value.style.display = 'initial'; - inputBoxContainer.style.display = 'none'; - dispose(toDispose); - } - }; - - toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => { - const isEscape = e.equals(KeyCode.Escape); - const isEnter = e.equals(KeyCode.Enter); - if (isEscape || isEnter) { - e.preventDefault(); - e.stopPropagation(); - wrapUp(isEnter); - } - })); - toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => { - wrapUp(true); - })); - toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'click', e => { - // Do not expand / collapse selected elements - e.preventDefault(); - e.stopPropagation(); - })); - }; - - return { expression, name, value, label, enableInputBox, inputBoxContainer, toDispose }; + return { expression, name, value, label, inputBoxContainer, toDispose: Disposable.None }; } renderElement(node: ITreeNode, index: number, data: IExpressionTemplateData): void { + data.toDispose.dispose(); + data.toDispose = Disposable.None; const { element } = node; if (element === this.debugService.getViewModel().getSelectedExpression() || (element instanceof Variable && element.errorMessage)) { const options = this.getInputBoxOptions(element); if (options) { - data.enableInputBox(options); + data.toDispose = this.renderInputBox(data.name, data.value, data.inputBoxContainer, options); return; } } this.renderExpression(element, data, createMatches(node.filterData)); } + renderInputBox(nameElement: HTMLElement, valueElement: HTMLElement, inputBoxContainer: HTMLElement, options: IInputBoxOptions): IDisposable { + nameElement.style.display = 'none'; + valueElement.style.display = 'none'; + inputBoxContainer.style.display = 'initial'; + + const inputBox = new InputBox(inputBoxContainer, this.contextViewService, options); + const styler = attachInputBoxStyler(inputBox, this.themeService); + + inputBox.value = replaceWhitespace(options.initialValue); + inputBox.focus(); + inputBox.select(); + + const done = once((success: boolean, finishEditing: boolean) => { + nameElement.style.display = 'initial'; + valueElement.style.display = 'initial'; + inputBoxContainer.style.display = 'none'; + const value = inputBox.value; + dispose(toDispose); + + if (finishEditing) { + this.debugService.getViewModel().setSelectedExpression(undefined); + options.onFinish(value, success); + } + }); + + const toDispose = [ + inputBox, + dom.addStandardDisposableListener(inputBox.inputElement, dom.EventType.KEY_DOWN, (e: IKeyboardEvent) => { + const isEscape = e.equals(KeyCode.Escape); + const isEnter = e.equals(KeyCode.Enter); + if (isEscape || isEnter) { + e.preventDefault(); + e.stopPropagation(); + done(isEnter, true); + } + }), + dom.addDisposableListener(inputBox.inputElement, dom.EventType.BLUR, (e) => { + done(true, true); + }), + dom.addDisposableListener(inputBox.inputElement, dom.EventType.CLICK, e => { + // Do not expand / collapse selected elements + e.preventDefault(); + e.stopPropagation(); + }), + styler + ]; + + return toDisposable(() => { + done(false, false); + }); + } + protected abstract renderExpression(expression: IExpression, data: IExpressionTemplateData, highlights: IHighlight[]): void; protected abstract getInputBoxOptions(expression: IExpression): IInputBoxOptions | undefined; + disposeElement(node: ITreeNode, index: number, templateData: IExpressionTemplateData): void { + templateData.toDispose.dispose(); + } + disposeTemplate(templateData: IExpressionTemplateData): void { - dispose(templateData.toDispose); + templateData.toDispose.dispose(); } } diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index 0e2f8fd6df4..7ee2d0fcaca 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -69,7 +69,16 @@ export class WatchExpressionsView extends ViewPane { ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"), accessibilityProvider: new WatchExpressionsAccessibilityProvider(), identityProvider: { getId: (element: IExpression) => element.getId() }, - keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IExpression) => e }, + keyboardNavigationLabelProvider: { + getKeyboardNavigationLabel: (e: IExpression) => { + if (e === this.debugService.getViewModel().getSelectedExpression()) { + // Don't filter input box + return undefined; + } + + return e; + } + }, dnd: new WatchExpressionsDragAndDrop(this.debugService), overrideStyles: { listBackground: SIDE_BAR_BACKGROUND From 1e061ff1d4a5d98a53184fb0909f7244d1bb0a3d Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Sun, 12 Jan 2020 02:50:09 -0500 Subject: [PATCH 223/843] :lipstick: --- src/vs/workbench/contrib/debug/browser/baseDebugView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index 160c2c8f3cd..3d6eafae60a 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -213,7 +213,7 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer { + dom.addDisposableListener(inputBox.inputElement, dom.EventType.BLUR, () => { done(true, true); }), dom.addDisposableListener(inputBox.inputElement, dom.EventType.CLICK, e => { From 39af627463e7eecfb5c59149ed67bb1afda3564a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 13 Jan 2020 09:15:36 -0800 Subject: [PATCH 224/843] has -> had --- src/vs/workbench/contrib/terminal/browser/terminalPanel.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 2a0408ff556..2c685281dc9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -97,13 +97,13 @@ export class TerminalPanel extends Panel { this._register(this.onDidChangeVisibility(visible => { if (visible) { - const hasTerminals = this._terminalService.terminalInstances.length > 0; - if (!hasTerminals) { + const hadTerminals = this._terminalService.terminalInstances.length > 0; + if (!hadTerminals) { this._terminalService.createTerminal(); } this._updateFont(); this._updateTheme(); - if (hasTerminals) { + if (hadTerminals) { this._terminalService.getActiveTab()?.setVisible(visible); } } From 16b75e33070d7c1e04c2e4af1e2f54bce7dfa41e Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Mon, 13 Jan 2020 10:15:40 -0800 Subject: [PATCH 225/843] Enhance views service (#87505) Add view-moving functionality to viewsservice which has been renamed to viewdescriptorservice. also includes outline view moving --- .../parts/activitybar/activitybarPart.ts | 10 +- .../browser/parts/panel/panelPart.ts | 8 +- src/vs/workbench/browser/parts/views/views.ts | 283 ++++++++++++------ src/vs/workbench/common/views.ts | 29 ++ .../outline/browser/outline.contribution.ts | 77 ++++- .../quickopen/browser/viewPickerHandler.ts | 5 +- .../test/browser/parts/views/views.test.ts | 30 +- 7 files changed, 328 insertions(+), 114 deletions(-) diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 29ac27a90b6..9cdf544c792 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -26,7 +26,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { URI, UriComponents } from 'vs/base/common/uri'; import { ToggleCompositePinnedAction, ICompositeBarColors, ActivityAction, ICompositeActivity } from 'vs/workbench/browser/parts/compositeBarActions'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; -import { IViewsService, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewDescriptorCollection } from 'vs/workbench/common/views'; +import { IViewDescriptorService, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewDescriptorCollection } from 'vs/workbench/common/views'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types'; @@ -88,7 +88,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { @IThemeService themeService: IThemeService, @IStorageService private readonly storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, - @IViewsService private readonly viewsService: IViewsService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService, @@ -189,7 +189,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (viewletDescriptor) { const viewContainer = this.getViewContainer(viewletDescriptor.id); if (viewContainer?.hideIfEmpty) { - const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); if (viewDescriptors?.activeViewDescriptors.length === 0) { this.hideComposite(viewletDescriptor.id); // Update the composite bar by hiding } @@ -410,7 +410,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.enableCompositeActions(viewlet); const viewContainer = this.getViewContainer(viewlet.id); if (viewContainer?.hideIfEmpty) { - const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); if (viewDescriptors) { this.onDidChangeActiveViews(viewlet, viewDescriptors); this.viewletDisposables.set(viewlet.id, viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(viewlet, viewDescriptors))); @@ -551,7 +551,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (viewlet) { const views: { when: string | undefined }[] = []; if (viewContainer) { - const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); if (viewDescriptors) { for (const { when } of viewDescriptors.allViewDescriptors) { views.push({ when: when ? when.serialize() : undefined }); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 354466a8da0..c381e6021cd 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -33,7 +33,7 @@ import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/con import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewsService, IViewDescriptorCollection } from 'vs/workbench/common/views'; +import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, IViewDescriptorCollection } from 'vs/workbench/common/views'; interface ICachedPanel { id: string; @@ -98,9 +98,9 @@ export class PanelPart extends CompositePart implements IPanelService { @IKeybindingService keybindingService: IKeybindingService, @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IExtensionService private readonly extensionService: IExtensionService, - @IViewsService private readonly viewsService: IViewsService, ) { super( notificationService, @@ -184,7 +184,7 @@ export class PanelPart extends CompositePart implements IPanelService { this.enableCompositeActions(panel); const viewContainer = this.getViewContainer(panel.id); if (viewContainer?.hideIfEmpty) { - const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); if (viewDescriptors) { this.onDidChangeActiveViews(panel, viewDescriptors); this.panelDisposables.set(panel.id, viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(panel, viewDescriptors))); @@ -295,7 +295,7 @@ export class PanelPart extends CompositePart implements IPanelService { if (panelDescriptor) { const viewContainer = this.getViewContainer(panelDescriptor.id); if (viewContainer?.hideIfEmpty) { - const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); if (viewDescriptors?.activeViewDescriptors.length === 0) { this.hideComposite(panelDescriptor.id); // Update the composite bar by hiding } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index a3b9148647a..91ca9c36bd0 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/views'; import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IViewsService, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, IViewDescriptorCollection, IViewsRegistry } from 'vs/workbench/common/views'; +import { IViewDescriptorService, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, IViewDescriptorCollection, IViewsRegistry, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -21,6 +21,8 @@ import { values } from 'vs/base/common/map'; import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { toggleClass, addClass } from 'vs/base/browser/dom'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IPaneComposite } from 'vs/workbench/common/panecomposite'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; function filterViewRegisterEvent(container: ViewContainer, event: Event<{ viewContainer: ViewContainer, views: IViewDescriptor[]; }>): Event { return Event.chain(event) @@ -93,14 +95,14 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl constructor( container: ViewContainer, - @IContextKeyService private readonly contextKeyService: IContextKeyService + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService ) { super(); - const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); - const onRelevantViewsRegistered = filterViewRegisterEvent(container, viewsRegistry.onViewsRegistered); + const onRelevantViewsRegistered = filterViewRegisterEvent(container, this.viewDescriptorService.onViewsRegistered); this._register(onRelevantViewsRegistered(this.onViewsRegistered, this)); - const onRelevantViewsMoved = filterViewMoveEvent(container, viewsRegistry.onDidChangeContainer); + const onRelevantViewsMoved = filterViewMoveEvent(container, this.viewDescriptorService.onDidChangeContainer); this._register(onRelevantViewsMoved(({ added, removed }) => { if (isNonEmptyArray(added)) { this.onViewsRegistered(added); @@ -110,13 +112,14 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl } })); - const onRelevantViewsDeregistered = filterViewRegisterEvent(container, viewsRegistry.onViewsDeregistered); + const onRelevantViewsDeregistered = filterViewRegisterEvent(container, this.viewDescriptorService.onViewsDeregistered); this._register(onRelevantViewsDeregistered(this.onViewsDeregistered, this)); const onRelevantContextChange = Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys)); this._register(onRelevantContextChange(this.onContextChanged, this)); - this.onViewsRegistered(viewsRegistry.getViews(container)); + + this.onViewsRegistered(this.viewDescriptorService.getViews(container)); } private onViewsRegistered(viewDescriptors: IViewDescriptor[]): void { @@ -249,7 +252,7 @@ export class ContributableViewsModel extends Disposable { constructor( container: ViewContainer, - viewsService: IViewsService, + viewsService: IViewDescriptorService, protected viewStates = new Map(), ) { super(); @@ -487,13 +490,13 @@ export class PersistentContributableViewsModel extends ContributableViewsModel { constructor( container: ViewContainer, viewletStateStorageId: string, - @IViewsService viewsService: IViewsService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IStorageService storageService: IStorageService, ) { const globalViewsStateStorageId = `${viewletStateStorageId}.hidden`; const viewStates = PersistentContributableViewsModel.loadViewsStates(viewletStateStorageId, globalViewsStateStorageId, storageService); - super(container, viewsService, viewStates); + super(container, viewDescriptorService, viewStates); this.workspaceViewsStateStorageId = viewletStateStorageId; this.globalViewsStateStorageId = globalViewsStateStorageId; @@ -624,105 +627,45 @@ export class PersistentContributableViewsModel extends ContributableViewsModel { } } -export class ViewsService extends Disposable implements IViewsService { +export class ViewOpenerService extends Disposable implements IViewsService { _serviceBrand: undefined; - private readonly viewDescriptorCollections: Map; + private readonly viewContainersRegistry: IViewContainersRegistry; private readonly viewDisposable: Map; - private readonly activeViewContextKeys: Map>; constructor( - @IViewletService private readonly viewletService: IViewletService, - @IContextKeyService private readonly contextKeyService: IContextKeyService + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @IPanelService private readonly panelService: IPanelService, + @IViewletService private readonly viewletService: IViewletService ) { super(); - this.viewDescriptorCollections = new Map(); + this.viewContainersRegistry = Registry.as(ViewExtensions.ViewContainersRegistry); this.viewDisposable = new Map(); - this.activeViewContextKeys = new Map>(); - const viewContainersRegistry = Registry.as(ViewExtensions.ViewContainersRegistry); - const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); - viewContainersRegistry.all.forEach(viewContainer => { - this.onDidRegisterViews(viewContainer, viewsRegistry.getViews(viewContainer)); - this.onDidRegisterViewContainer(viewContainer); - }); - this._register(viewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(viewContainer, views))); - this._register(viewsRegistry.onViewsDeregistered(({ views }) => this.onDidDeregisterViews(views))); - this._register(viewsRegistry.onDidChangeContainer(({ views, to }) => { this.onDidDeregisterViews(views); this.onDidRegisterViews(to, views); })); this._register(toDisposable(() => { this.viewDisposable.forEach(disposable => disposable.dispose()); this.viewDisposable.clear(); })); - this._register(viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onDidRegisterViewContainer(viewContainer))); - this._register(viewContainersRegistry.onDidDeregister(({ viewContainer }) => this.onDidDeregisterViewContainer(viewContainer))); - this._register(toDisposable(() => { - this.viewDescriptorCollections.forEach(({ disposable }) => disposable.dispose()); - this.viewDescriptorCollections.clear(); - })); + + this._register(this.viewDescriptorService.onViewsRegistered(({ views, viewContainer }) => this.onViewsRegistered(views, viewContainer))); + this._register(this.viewDescriptorService.onViewsDeregistered(({ views, viewContainer }) => this.onViewsDeregistered(views, viewContainer))); } - getViewDescriptors(container: ViewContainer): IViewDescriptorCollection | null { - const registeredViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).get(container.id); - if (registeredViewContainer) { - let viewDescriptorCollectionItem = this.viewDescriptorCollections.get(registeredViewContainer); - if (!viewDescriptorCollectionItem) { - // Create and register the collection if does not exist - this.onDidRegisterViewContainer(registeredViewContainer); - viewDescriptorCollectionItem = this.viewDescriptorCollections.get(registeredViewContainer); - } - return viewDescriptorCollectionItem!.viewDescriptorCollection; - } - return null; - } - - async openView(id: string, focus: boolean): Promise { - const viewContainer = Registry.as(ViewExtensions.ViewsRegistry).getViewContainer(id); - if (viewContainer) { - const viewletDescriptor = this.viewletService.getViewlet(viewContainer.id); - if (viewletDescriptor) { - const viewlet = await this.viewletService.openViewlet(viewletDescriptor.id, focus) as IViewsViewlet | null; - if (viewlet && viewlet.openView) { - return viewlet.openView(id, focus); - } - } + private onViewsRegistered(views: IViewDescriptor[], container: ViewContainer): void { + const location = this.viewContainersRegistry.getViewContainerLocation(container); + if (location === undefined) { + return; } - return null; - } - - private onDidRegisterViewContainer(viewContainer: ViewContainer): void { - const disposables = new DisposableStore(); - const viewDescriptorCollection = disposables.add(new ViewDescriptorCollection(viewContainer, this.contextKeyService)); - - this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] }); - viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed), this, disposables); - - this.viewDescriptorCollections.set(viewContainer, { viewDescriptorCollection, disposable: disposables }); - } - - private onDidDeregisterViewContainer(viewContainer: ViewContainer): void { - const viewDescriptorCollectionItem = this.viewDescriptorCollections.get(viewContainer); - if (viewDescriptorCollectionItem) { - viewDescriptorCollectionItem.disposable.dispose(); - this.viewDescriptorCollections.delete(viewContainer); - } - } - - private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[]; }): void { - added.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(true)); - removed.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(false)); - } - - private onDidRegisterViews(container: ViewContainer, views: IViewDescriptor[]): void { - const viewlet = this.viewletService.getViewlet(container.id); + const composite = this.getComposite(container.id, location); for (const viewDescriptor of views) { const disposables = new DisposableStore(); const command: ICommandAction = { id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`, title: { original: `Focus on ${viewDescriptor.name} View`, value: localize('focus view', "Focus on {0} View", viewDescriptor.name) }, - category: viewlet ? viewlet.name : localize('view category', "View"), + category: composite ? composite.name : localize('view category', "View"), }; const when = ContextKeyExpr.has(`${viewDescriptor.id}.active`); @@ -750,7 +693,7 @@ export class ViewsService extends Disposable implements IViewsService { } } - private onDidDeregisterViews(views: IViewDescriptor[]): void { + private onViewsDeregistered(views: IViewDescriptor[], container: ViewContainer): void { for (const view of views) { const disposable = this.viewDisposable.get(view); if (disposable) { @@ -760,6 +703,171 @@ export class ViewsService extends Disposable implements IViewsService { } } + + private async openComposite(compositeId: string, location: ViewContainerLocation, focus?: boolean): Promise { + if (location === ViewContainerLocation.Sidebar) { + return this.viewletService.openViewlet(compositeId, focus); + } else if (location === ViewContainerLocation.Panel) { + return this.panelService.openPanel(compositeId, focus) as IPaneComposite; + } + return undefined; + } + + private getComposite(compositeId: string, location: ViewContainerLocation): { id: string, name: string } | undefined { + if (location === ViewContainerLocation.Sidebar) { + return this.viewletService.getViewlet(compositeId); + } else if (location === ViewContainerLocation.Panel) { + return this.panelService.getPanel(compositeId); + } + + return undefined; + } + + async openView(id: string, focus: boolean): Promise { + const viewContainer = this.viewDescriptorService.getViewContainer(id); + if (viewContainer) { + const location = this.viewContainersRegistry.getViewContainerLocation(viewContainer); + const compositeDescriptor = this.getComposite(viewContainer.id, location!); + if (compositeDescriptor) { + const paneComposite = await this.openComposite(compositeDescriptor.id, location!, focus) as IPaneComposite | undefined; + if (paneComposite && paneComposite.openView) { + return paneComposite.openView(id, focus); + } + } + } + + return null; + } +} + +export class ViewDescriptorService extends Disposable implements IViewDescriptorService { + + _serviceBrand: undefined; + + private readonly _onViewsRegistered: Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }>()); + readonly onViewsRegistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._onViewsRegistered.event; + + private readonly _onViewsDeregistered: Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }>()); + readonly onViewsDeregistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._onViewsDeregistered.event; + + private readonly _onDidChangeContainer: Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>()); + readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._onDidChangeContainer.event; + + private readonly viewDescriptorCollections: Map; + private readonly viewContainers: Map; + private readonly activeViewContextKeys: Map>; + + private readonly viewsRegistry: IViewsRegistry; + private readonly viewContainersRegistry: IViewContainersRegistry; + + constructor( + @IContextKeyService private readonly contextKeyService: IContextKeyService + ) { + super(); + + this.viewDescriptorCollections = new Map(); + this.viewContainers = new Map(); + this.activeViewContextKeys = new Map>(); + + this.viewContainersRegistry = Registry.as(ViewExtensions.ViewContainersRegistry); + this.viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); + this.viewContainersRegistry.all.forEach(viewContainer => { + this.onDidRegisterViews(viewContainer, this.viewsRegistry.getViews(viewContainer)); + this.onDidRegisterViewContainer(viewContainer); + }); + this._register(this.viewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(viewContainer, views))); + this._register(this.viewsRegistry.onViewsDeregistered(({ views, viewContainer }) => this.onDidDeregisterViews(viewContainer, views))); + this._register(this.viewsRegistry.onDidChangeContainer(({ views, from, to }) => { this.onDidDeregisterViews(from, views); this.onDidRegisterViews(to, views); this._onDidChangeContainer.fire({ views, from, to }); })); + + this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onDidRegisterViewContainer(viewContainer))); + this._register(this.viewContainersRegistry.onDidDeregister(({ viewContainer }) => this.onDidDeregisterViewContainer(viewContainer))); + this._register(toDisposable(() => { + this.viewDescriptorCollections.forEach(({ disposable }) => disposable.dispose()); + this.viewDescriptorCollections.clear(); + })); + } + + getViewContainer(viewId: string): ViewContainer | null { + return this.viewContainers.get(viewId) ?? null; + } + + getViewDescriptors(container: ViewContainer): IViewDescriptorCollection | null { + const registeredViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).get(container.id); + if (registeredViewContainer) { + let viewDescriptorCollectionItem = this.viewDescriptorCollections.get(registeredViewContainer); + if (!viewDescriptorCollectionItem) { + // Create and register the collection if does not exist + this.onDidRegisterViewContainer(registeredViewContainer); + viewDescriptorCollectionItem = this.viewDescriptorCollections.get(registeredViewContainer); + } + return viewDescriptorCollectionItem!.viewDescriptorCollection; + } + return null; + } + + moveViews(views: IViewDescriptor[], viewContainer: ViewContainer): void { + if (!views.length) { + return; + } + + const from = this.viewContainers.get(views[0].id); + const to = viewContainer; + + if (from && to && from !== to) { + this.onDidDeregisterViews(from, views); + this.onDidRegisterViews(viewContainer, views); + this._onDidChangeContainer.fire({ views, from, to }); + } + } + + getViews(container: ViewContainer): IViewDescriptor[] { + return this.viewsRegistry.getViews(container); + } + + private onDidRegisterViewContainer(viewContainer: ViewContainer): void { + const disposables = new DisposableStore(); + const viewDescriptorCollection = disposables.add(new ViewDescriptorCollection(viewContainer, this.contextKeyService, this)); + + this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] }); + viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed), this, disposables); + + this.viewDescriptorCollections.set(viewContainer, { viewDescriptorCollection, disposable: disposables }); + } + + private onDidDeregisterViewContainer(viewContainer: ViewContainer): void { + const viewDescriptorCollectionItem = this.viewDescriptorCollections.get(viewContainer); + if (viewDescriptorCollectionItem) { + viewDescriptorCollectionItem.disposable.dispose(); + this.viewDescriptorCollections.delete(viewContainer); + } + } + + private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[]; }): void { + added.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(true)); + removed.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(false)); + } + + private onDidRegisterViews(container: ViewContainer, views: IViewDescriptor[]): void { + const location = this.viewContainersRegistry.getViewContainerLocation(container); + if (location === undefined) { + return; + } + + for (const viewDescriptor of views) { + this.viewContainers.set(viewDescriptor.id, container); + } + + this._onViewsRegistered.fire({ views, viewContainer: container }); + } + + private onDidDeregisterViews(container: ViewContainer, views: IViewDescriptor[]): void { + for (const view of views) { + this.viewContainers.delete(view.id); + } + + this._onViewsDeregistered.fire({ views, viewContainer: container }); + } + private getOrCreateActiveViewContextKey(viewDescriptor: IViewDescriptor): IContextKey { const activeContextKeyId = `${viewDescriptor.id}.active`; let contextKey = this.activeViewContextKeys.get(activeContextKeyId); @@ -784,4 +892,5 @@ export function createFileIconThemableTreeContainerScope(container: HTMLElement, return themeService.onDidFileIconThemeChange(onDidChangeFileIconTheme); } -registerSingleton(IViewsService, ViewsService); +registerSingleton(IViewDescriptorService, ViewDescriptorService); +registerSingleton(IViewsService, ViewOpenerService); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 174df04dff6..72f90b88cd6 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -100,6 +100,11 @@ export interface IViewContainersRegistry { * Returns all view containers in the given location */ getViewContainers(location: ViewContainerLocation): ViewContainer[]; + + /** + * Returns the view container location + */ + getViewContainerLocation(container: ViewContainer): ViewContainerLocation | undefined; } interface ViewOrderDelegate { @@ -157,6 +162,10 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe getViewContainers(location: ViewContainerLocation): ViewContainer[] { return [...(this.viewContainers.get(location) || [])]; } + + getViewContainerLocation(container: ViewContainer): ViewContainerLocation | undefined { + return keys(this.viewContainers).filter(location => this.getViewContainers(location).filter(viewContainer => viewContainer.id === container.id).length > 0)[0]; + } } Registry.add(Extensions.ViewContainersRegistry, new ViewContainersRegistryImpl()); @@ -336,14 +345,34 @@ export interface IViewsViewlet extends IViewlet { } +export const IViewDescriptorService = createDecorator('viewDescriptorService'); export const IViewsService = createDecorator('viewsService'); + export interface IViewsService { _serviceBrand: undefined; openView(id: string, focus?: boolean): Promise; +} + + +export interface IViewDescriptorService { + + _serviceBrand: undefined; + + readonly onViewsRegistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }>; + + readonly onViewsDeregistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }>; + + readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>; + + moveViews(views: IViewDescriptor[], viewContainer: ViewContainer): void; + + getViews(container: ViewContainer): IViewDescriptor[]; getViewDescriptors(container: ViewContainer): IViewDescriptorCollection | null; + + getViewContainer(viewId: string): ViewContainer | null; } // Custom views diff --git a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts index d95ee1360d0..bbc5693905b 100644 --- a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts +++ b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts @@ -4,16 +4,56 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions } from 'vs/workbench/common/views'; +import { IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions, ViewContainer, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService, IViewsService } from 'vs/workbench/common/views'; import { OutlinePane } from './outlinePane'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { OutlineConfigKeys, OutlineViewId } from 'vs/editor/contrib/documentSymbols/outline'; import { VIEW_CONTAINER } from 'vs/workbench/contrib/files/browser/explorerViewlet'; +import { Action } from 'vs/base/common/actions'; +import { IWorkbenchActionRegistry, Extensions as ActionsExtensions } from 'vs/workbench/common/actions'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; // import './outlineNavigation'; +export const PANEL_ID = 'panel.view.outline'; + +export class OutlineViewPaneContainer extends ViewPaneContainer { + constructor( + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, + @ITelemetryService telemetryService: ITelemetryService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IStorageService protected storageService: IStorageService, + @IConfigurationService configurationService: IConfigurationService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService, + ) { + super(PANEL_ID, `${PANEL_ID}.state`, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService); + } +} + +export const VIEW_CONTAINER_PANEL: ViewContainer = + Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ + id: PANEL_ID, + ctorDescriptor: new SyncDescriptor(OutlineViewPaneContainer), + name: localize('name', "Outline"), + hideIfEmpty: true + }, ViewContainerLocation.Panel); + + const _outlineDesc = { id: OutlineViewId, name: localize('name', "Outline"), @@ -28,6 +68,41 @@ const _outlineDesc = { Registry.as(ViewExtensions.ViewsRegistry).registerViews([_outlineDesc], VIEW_CONTAINER); +let inPanel = false; + +export class ToggleOutlinePositionAction extends Action { + + static ID = 'outline.view.togglePosition'; + static LABEL = 'Toggle Outline View Position'; + + constructor( + id: string, + label: string, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @IViewsService private readonly viewsService: IViewsService + ) { + super(id, label, '', true); + } + + async run(): Promise { + if (!inPanel) { + this.viewDescriptorService.moveViews([_outlineDesc], VIEW_CONTAINER_PANEL); + this.viewsService.openView(OutlineViewId, true); + inPanel = true; + } else { + this.viewDescriptorService.moveViews([_outlineDesc], VIEW_CONTAINER); + this.viewsService.openView(OutlineViewId, true); + + inPanel = false; + } + + } +} + +Registry.as(ActionsExtensions.WorkbenchActions) + .registerWorkbenchAction(SyncActionDescriptor.create(ToggleOutlinePositionAction, ToggleOutlinePositionAction.ID, ToggleOutlinePositionAction.LABEL), 'Show Release Notes'); + + Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ 'id': 'outline', 'order': 117, diff --git a/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts b/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts index 587dc075b24..eefa4456728 100644 --- a/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts @@ -16,7 +16,7 @@ import { Action } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { fuzzyContains, stripWildcards } from 'vs/base/common/strings'; import { matchesFuzzy } from 'vs/base/common/filters'; -import { IViewsRegistry, ViewContainer, IViewsService, IViewContainersRegistry, Extensions as ViewExtensions } from 'vs/workbench/common/views'; +import { IViewsRegistry, ViewContainer, IViewDescriptorService, IViewContainersRegistry, Extensions as ViewExtensions, IViewsService } from 'vs/workbench/common/views'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -70,6 +70,7 @@ export class ViewPickerHandler extends QuickOpenHandler { constructor( @IViewletService private readonly viewletService: IViewletService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IViewsService private readonly viewsService: IViewsService, @IOutputService private readonly outputService: IOutputService, @ITerminalService private readonly terminalService: ITerminalService, @@ -197,7 +198,7 @@ export class ViewPickerHandler extends QuickOpenHandler { private hasToShowViewlet(viewlet: ViewletDescriptor): boolean { const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).get(viewlet.id); if (viewContainer?.hideIfEmpty) { - const viewsCollection = this.viewsService.getViewDescriptors(viewContainer); + const viewsCollection = this.viewDescriptorService.getViewDescriptors(viewContainer); return !!viewsCollection && viewsCollection.activeViewDescriptors.length > 0; } return true; diff --git a/src/vs/workbench/test/browser/parts/views/views.test.ts b/src/vs/workbench/test/browser/parts/views/views.test.ts index a5244e4de17..49502d7bec9 100644 --- a/src/vs/workbench/test/browser/parts/views/views.test.ts +++ b/src/vs/workbench/test/browser/parts/views/views.test.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ContributableViewsModel, ViewsService, IViewState } from 'vs/workbench/browser/parts/views/views'; -import { IViewsRegistry, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views'; +import { ContributableViewsModel, ViewDescriptorService, IViewState } from 'vs/workbench/browser/parts/views/views'; +import { IViewsRegistry, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { move } from 'vs/base/common/arrays'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -38,14 +38,14 @@ class ViewDescriptorSequence { suite('ContributableViewsModel', () => { - let viewsService: IViewsService; + let viewDescriptorService: IViewDescriptorService; let contextKeyService: IContextKeyService; setup(() => { const instantiationService: TestInstantiationService = workbenchInstantiationService(); contextKeyService = instantiationService.createInstance(ContextKeyService); instantiationService.stub(IContextKeyService, contextKeyService); - viewsService = instantiationService.createInstance(ViewsService); + viewDescriptorService = instantiationService.createInstance(ViewDescriptorService); }); teardown(() => { @@ -53,12 +53,12 @@ suite('ContributableViewsModel', () => { }); test('empty model', function () { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); assert.equal(model.visibleViewDescriptors.length, 0); }); test('register/unregister', () => { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); assert.equal(model.visibleViewDescriptors.length, 0); @@ -84,7 +84,7 @@ suite('ContributableViewsModel', () => { }); test('when contexts', async function () { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); assert.equal(model.visibleViewDescriptors.length, 0); @@ -128,7 +128,7 @@ suite('ContributableViewsModel', () => { }); test('when contexts - multiple', async function () { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1' }; @@ -151,7 +151,7 @@ suite('ContributableViewsModel', () => { }); test('when contexts - multiple 2', async function () { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1', when: ContextKeyExpr.equals('showview1', true) }; @@ -174,7 +174,7 @@ suite('ContributableViewsModel', () => { }); test('setVisible', () => { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1', canToggleVisibility: true }; @@ -219,7 +219,7 @@ suite('ContributableViewsModel', () => { }); test('move', () => { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1' }; @@ -250,7 +250,7 @@ suite('ContributableViewsModel', () => { test('view states', async function () { const viewStates = new Map(); viewStates.set('view1', { visibleGlobal: false, collapsed: false, visibleWorkspace: undefined }); - const model = new ContributableViewsModel(container, viewsService, viewStates); + const model = new ContributableViewsModel(container, viewDescriptorService, viewStates); const seq = new ViewDescriptorSequence(model); assert.equal(model.visibleViewDescriptors.length, 0); @@ -270,7 +270,7 @@ suite('ContributableViewsModel', () => { test('view states and when contexts', async function () { const viewStates = new Map(); viewStates.set('view1', { visibleGlobal: false, collapsed: false, visibleWorkspace: undefined }); - const model = new ContributableViewsModel(container, viewsService, viewStates); + const model = new ContributableViewsModel(container, viewDescriptorService, viewStates); const seq = new ViewDescriptorSequence(model); assert.equal(model.visibleViewDescriptors.length, 0); @@ -300,7 +300,7 @@ suite('ContributableViewsModel', () => { test('view states and when contexts multiple views', async function () { const viewStates = new Map(); viewStates.set('view1', { visibleGlobal: false, collapsed: false, visibleWorkspace: undefined }); - const model = new ContributableViewsModel(container, viewsService, viewStates); + const model = new ContributableViewsModel(container, viewDescriptorService, viewStates); const seq = new ViewDescriptorSequence(model); assert.equal(model.visibleViewDescriptors.length, 0); @@ -344,7 +344,7 @@ suite('ContributableViewsModel', () => { }); test('remove event is not triggered if view was hidden and removed', async function () { - const model = new ContributableViewsModel(container, viewsService); + const model = new ContributableViewsModel(container, viewDescriptorService); const seq = new ViewDescriptorSequence(model); const viewDescriptor: IViewDescriptor = { From 85903e849feed2304687a7fd616a26cd5d86b2dd Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 13 Jan 2020 10:40:38 -0800 Subject: [PATCH 226/843] missed rename for viewsservice --- src/vs/workbench/browser/parts/views/views.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 91ca9c36bd0..b2911ed6663 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -627,7 +627,7 @@ export class PersistentContributableViewsModel extends ContributableViewsModel { } } -export class ViewOpenerService extends Disposable implements IViewsService { +export class ViewsService extends Disposable implements IViewsService { _serviceBrand: undefined; @@ -893,4 +893,4 @@ export function createFileIconThemableTreeContainerScope(container: HTMLElement, } registerSingleton(IViewDescriptorService, ViewDescriptorService); -registerSingleton(IViewsService, ViewOpenerService); +registerSingleton(IViewsService, ViewsService); From 9fa325deec33aa93c359e2e2e529c008596712ce Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 13 Jan 2020 10:58:53 -0800 Subject: [PATCH 227/843] Fix search editor URI malform error --- src/vs/workbench/contrib/search/browser/searchEditorCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts index 6d9e6ee65ee..07177d95ea8 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts @@ -85,7 +85,7 @@ export class SearchEditorInput extends EditorInput { } getResource(): URI { - return URI.from({ scheme: 'untitled', authority: 'search-editor', path: this.config.query, fragment: `${this.instanceNumber}` }); + return URI.from({ scheme: 'search-editor', fragment: `${this.instanceNumber}` }); } getName(): string { From 1de4563f26184c98d3297e8d058380a7258b50b4 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 13 Jan 2020 11:47:25 -0800 Subject: [PATCH 228/843] fix(debug): don't insert task boundaries between events (#88459) * fix(debug): don't insert task boundaries between events See discussion in https://github.com/microsoft/vscode-js-debug/issues/206. This PR adjusts logic such that we only assert task boundaries around requests and responses, rather than around every single event. I believe this will solve the primary case where misordering can happen, as given in the existing unit test and described more verbosely in the doc comment in this PR. A more conservative approach would be to only omit the boundary between events of the same type. That would be safer, but I browsing through the code I didn't see any cases where it looked like we could get tripped up by bucketing here (e.g. cases where we resolve a deferred promise in an event handler). Seems to fix the performance issue for me. * fixup! add unit tests for ordering --- .../debug/common/abstractDebugAdapter.ts | 102 ++++++++++++------ .../debug/test/common/rawDebugSession.test.ts | 52 +++++++++ 2 files changed, 121 insertions(+), 33 deletions(-) create mode 100644 src/vs/workbench/contrib/debug/test/common/rawDebugSession.test.ts diff --git a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts index f060ba6c665..93a76d52c1e 100644 --- a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts +++ b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts @@ -5,20 +5,19 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IDebugAdapter } from 'vs/workbench/contrib/debug/common/debug'; -import { timeout, Queue } from 'vs/base/common/async'; +import { timeout } from 'vs/base/common/async'; /** * Abstract implementation of the low level API for a debug adapter. * Missing is how this API communicates with the debug adapter. */ export abstract class AbstractDebugAdapter implements IDebugAdapter { - private sequence: number; private pendingRequests = new Map void>(); private requestCallback: ((request: DebugProtocol.Request) => void) | undefined; private eventCallback: ((request: DebugProtocol.Event) => void) | undefined; private messageCallback: ((message: DebugProtocol.ProtocolMessage) => void) | undefined; - private readonly queue = new Queue(); + private queue: DebugProtocol.ProtocolMessage[] = []; protected readonly _onError = new Emitter(); protected readonly _onExit = new Emitter(); @@ -64,8 +63,7 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { sendResponse(response: DebugProtocol.Response): void { if (response.seq > 0) { this._onError.fire(new Error(`attempt to send more than one response for command ${response.command}`)); - } - else { + } else { this.internalSend('response', response); } } @@ -107,35 +105,73 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { acceptMessage(message: DebugProtocol.ProtocolMessage): void { if (this.messageCallback) { this.messageCallback(message); + } else { + this.queue.push(message); + if (this.queue.length === 1) { + // first item = need to start processing loop + this.processQueue(); + } } - else { - this.queue.queue(() => { - switch (message.type) { - case 'event': - if (this.eventCallback) { - this.eventCallback(message); - } - break; - case 'request': - if (this.requestCallback) { - this.requestCallback(message); - } - break; - case 'response': - const response = message; - const clb = this.pendingRequests.get(response.request_seq); - if (clb) { - this.pendingRequests.delete(response.request_seq); - clb(response); - } - break; - } + } - // Artificially queueing protocol messages guarantees that any microtasks for - // previous message finish before next message is processed. This is essential - // to guarantee ordering when using promises anywhere along the call path. - return timeout(0); - }); + /** + * Returns whether we should insert a timeout between processing messageA + * and messageB. Artificially queueing protocol messages guarantees that any + * microtasks for previous message finish before next message is processed. + * This is essential ordering when using promises anywhere along the call path. + * + * For example, take the following, where `chooseAndSendGreeting` returns + * a person name and then emits a greeting event: + * + * ``` + * let person: string; + * adapter.onGreeting(() => console.log('hello', person)); + * person = await adapter.chooseAndSendGreeting(); + * ``` + * + * Because the event is dispatched synchronously, it may fire before person + * is assigned if they're processed in the same task. Inserting a task + * boundary avoids this issue. + */ + protected needsTaskBoundaryBetween(messageA: DebugProtocol.ProtocolMessage, messageB: DebugProtocol.ProtocolMessage) { + return messageA.type !== 'event' || messageB.type !== 'event'; + } + + /** + * Reads and dispatches items from the queue until it is empty. + */ + private async processQueue() { + let message: DebugProtocol.ProtocolMessage | undefined; + while (this.queue.length) { + if (!message || this.needsTaskBoundaryBetween(this.queue[0], message)) { + await timeout(0); + } + + message = this.queue.shift(); + if (!message) { + return; // may have been disposed of + } + + switch (message.type) { + case 'event': + if (this.eventCallback) { + this.eventCallback(message); + } + break; + case 'request': + if (this.requestCallback) { + this.requestCallback(message); + } + break; + case 'response': + const response = message; + const clb = this.pendingRequests.get(response.request_seq); + if (clb) { + this.pendingRequests.delete(response.request_seq); + clb(response); + } + break; + } } } @@ -172,6 +208,6 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { } dispose(): void { - this.queue.dispose(); + this.queue = []; } } diff --git a/src/vs/workbench/contrib/debug/test/common/rawDebugSession.test.ts b/src/vs/workbench/contrib/debug/test/common/rawDebugSession.test.ts new file mode 100644 index 00000000000..16c02f6cb02 --- /dev/null +++ b/src/vs/workbench/contrib/debug/test/common/rawDebugSession.test.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { MockDebugAdapter } from 'vs/workbench/contrib/debug/test/common/mockDebug'; +import { timeout } from 'vs/base/common/async'; + +suite('Debug - AbstractDebugAdapter', () => { + suite('event ordering', () => { + let adapter: MockDebugAdapter; + let output: string[]; + setup(() => { + adapter = new MockDebugAdapter(); + output = []; + adapter.onEvent(ev => { + output.push((ev as DebugProtocol.OutputEvent).body.output); + Promise.resolve().then(() => output.push('--end microtask--')); + }); + }); + + const evaluate = async (expression: string) => { + await new Promise(resolve => adapter.sendRequest('evaluate', { expression }, resolve)); + output.push(`=${expression}`); + Promise.resolve().then(() => output.push('--end microtask--')); + }; + + test('inserts task boundary before response', async () => { + await evaluate('before.foo'); + await timeout(0); + + assert.deepStrictEqual(output, ['before.foo', '--end microtask--', '=before.foo', '--end microtask--']); + }); + + test('inserts task boundary after response', async () => { + await evaluate('after.foo'); + await timeout(0); + + assert.deepStrictEqual(output, ['=after.foo', '--end microtask--', 'after.foo', '--end microtask--']); + }); + + test('does not insert boundaries between events', async () => { + adapter.sendEventBody('output', { output: 'a' }); + adapter.sendEventBody('output', { output: 'b' }); + adapter.sendEventBody('output', { output: 'c' }); + await timeout(0); + + assert.deepStrictEqual(output, ['a', 'b', 'c', '--end microtask--', '--end microtask--', '--end microtask--']); + }); + }); +}); From 7ef5ef6e952e4eaeb32d0258612f08fbb439b849 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 13 Jan 2020 21:06:57 +0100 Subject: [PATCH 229/843] polish html sem highlighting --- .../server/src/modes/javascriptSemanticTokens.ts | 4 ++-- .../server/src/test/semanticTokens.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts index 3c7b620ab8c..38a87159a32 100644 --- a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts +++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts @@ -17,7 +17,7 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current const typeChecker = program.getTypeChecker(); function visit(node: ts.Node) { - if (node.kind === ts.SyntaxKind.Identifier) { + if (ts.isIdentifier(node)) { const symbol = typeChecker.getSymbolAtLocation(node); if (symbol) { let typeIdx = classifySymbol(symbol); @@ -76,7 +76,7 @@ function classifySymbol(symbol: ts.Symbol) { } } const decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; - return tokenFromDeclarationMapping[decl.kind]; + return decl && tokenFromDeclarationMapping[decl.kind]; } diff --git a/extensions/html-language-features/server/src/test/semanticTokens.test.ts b/extensions/html-language-features/server/src/test/semanticTokens.test.ts index 384d1467d0b..ea3777e84e9 100644 --- a/extensions/html-language-features/server/src/test/semanticTokens.test.ts +++ b/extensions/html-language-features/server/src/test/semanticTokens.test.ts @@ -147,7 +147,7 @@ suite('HTML Semantic Tokens', () => { /*3*/' const f = 9;', /*4*/' class A { static readonly t = 9; static url: URL; }', /*5*/' const enum E { A = 9, B = A + 1 }', - /*6*/' const x = f + A.t + A.url.origin;', + /*6*/' console.log(f + A.t + A.url.origin);', /*7*/'', /*8*/'', /*9*/'', @@ -156,7 +156,7 @@ suite('HTML Semantic Tokens', () => { t(3, 8, 1, 'variable.declaration.readonly'), t(4, 8, 1, 'class.declaration'), t(4, 28, 1, 'property.declaration.static.readonly'), t(4, 42, 3, 'property.declaration.static'), t(4, 47, 3, 'interface'), t(5, 13, 1, 'enum.declaration'), t(5, 17, 1, 'property.declaration.readonly'), t(5, 24, 1, 'property.declaration.readonly'), t(5, 28, 1, 'property.readonly'), - t(6, 8, 1, 'variable.declaration.readonly'), t(6, 12, 1, 'variable.readonly'), t(6, 16, 1, 'class'), t(6, 18, 1, 'property.static.readonly'), t(6, 22, 1, 'class'), t(6, 24, 3, 'property.static'), t(6, 28, 6, 'property.readonly'), + t(6, 2, 7, 'variable'), t(6, 10, 3, 'member'), t(6, 14, 1, 'variable.readonly'), t(6, 18, 1, 'class'), t(6, 20, 1, 'property.static.readonly'), t(6, 24, 1, 'class'), t(6, 26, 3, 'property.static'), t(6, 30, 6, 'property.readonly'), ]); }); From 80c6acdf312c6486e12c9629602d9783734db2b7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 13 Jan 2020 21:06:05 +0100 Subject: [PATCH 230/843] fix issue with accessibility provider, #88554 --- .../contrib/bulkEdit/browser/bulkEditTree.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 188c0569442..bbd92f7b31a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -118,38 +118,44 @@ export class BulkEditAccessibilityProvider implements IAccessibilityProvider 0) { if (element.edit.type & BulkFileOperationType.Rename && element.edit.newUri) { return localize( - 'area.renameAndEdit', "Renaming {0} to {1}, also making text edits", + 'aria.renameAndEdit', "Renaming {0} to {1}, also making text edits", this._labelService.getUriLabel(element.edit.uri, { relative: true }), this._labelService.getUriLabel(element.edit.newUri, { relative: true }) ); } else if (element.edit.type & BulkFileOperationType.Create) { return localize( - 'area.createAndEdit', "Creating {0}, also making text edits", + 'aria.createAndEdit', "Creating {0}, also making text edits", this._labelService.getUriLabel(element.edit.uri, { relative: true }) ); } else if (element.edit.type & BulkFileOperationType.Delete) { return localize( - 'area.deleteAndEdit', "Deleting {0}, also making text edits", + 'aria.deleteAndEdit', "Deleting {0}, also making text edits", + this._labelService.getUriLabel(element.edit.uri, { relative: true }), + ); + } else { + return localize( + 'aria.editOnly', "{0}, making text edits", this._labelService.getUriLabel(element.edit.uri, { relative: true }), ); } + } else { if (element.edit.type & BulkFileOperationType.Rename && element.edit.newUri) { return localize( - 'area.rename', "Renaming {0} to {1}", + 'aria.rename', "Renaming {0} to {1}", this._labelService.getUriLabel(element.edit.uri, { relative: true }), this._labelService.getUriLabel(element.edit.newUri, { relative: true }) ); } else if (element.edit.type & BulkFileOperationType.Create) { return localize( - 'area.create', "Creating {0}", + 'aria.create', "Creating {0}", this._labelService.getUriLabel(element.edit.uri, { relative: true }) ); } else if (element.edit.type & BulkFileOperationType.Delete) { return localize( - 'area.delete', "Deleting {0}", + 'aria.delete', "Deleting {0}", this._labelService.getUriLabel(element.edit.uri, { relative: true }), ); } @@ -159,7 +165,7 @@ export class BulkEditAccessibilityProvider implements IAccessibilityProvider 0 && element.inserting.length > 0) { // edit: replace - return localize('aria.replace', "line {0}, replacing {1} with {0}", element.edit.edit.range.startLineNumber, element.selecting, element.inserting); + return localize('aria.replace', "line {0}, replacing {1} with {2}", element.edit.edit.range.startLineNumber, element.selecting, element.inserting); } else if (element.selecting.length > 0 && element.inserting.length === 0) { // edit: delete return localize('aria.del', "line {0}, removing {1}", element.edit.edit.range.startLineNumber, element.selecting); From 1ea4d14894bec4809ed21132c8c3d8346e79deb7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 13 Jan 2020 21:17:21 +0100 Subject: [PATCH 231/843] add AriaProvider, #88553 --- .../contrib/bulkEdit/browser/bulkEditPane.ts | 3 ++- .../contrib/bulkEdit/browser/bulkEditTree.ts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 13e60a3218a..d0310fcc727 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -6,7 +6,7 @@ import 'vs/css!./bulkEdit'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; -import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; +import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider, BulkEditAriaProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; @@ -97,6 +97,7 @@ export class BulkEditPane extends ViewPane { this._instaService.createInstance(BulkEditDataSource), { accessibilityProvider: this._instaService.createInstance(BulkEditAccessibilityProvider), + ariaProvider: new BulkEditAriaProvider(), identityProvider: new BulkEditIdentityProvider(), expandOnlyOnTwistieClick: true, multipleSelectionSupport: false diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index bbd92f7b31a..2488030bcfa 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -20,6 +20,7 @@ import { FileKind } from 'vs/platform/files/common/files'; import { localize } from 'vs/nls'; import { ILabelService } from 'vs/platform/label/common/label'; import type { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import type { IAriaProvider } from 'vs/base/browser/ui/list/listView'; // --- VIEW MODEL @@ -192,6 +193,21 @@ export class BulkEditIdentityProvider implements IIdentityProvider { + + getSetSize(_element: BulkEditElement, _index: number, listLength: number): number { + return listLength; + } + + getPosInSet(_element: BulkEditElement, index: number): number { + return index; + } + + getRole?(_element: BulkEditElement): string { + return 'checkbox'; + } +} + // --- RENDERER class FileElementTemplate { From 2bbe9c533005e97f163ed38e1cfe85ea23787aeb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 13 Jan 2020 12:47:04 -0800 Subject: [PATCH 232/843] Only override editor opening for resource that have potential custom editors For #88525 --- .../contrib/customEditor/browser/customEditors.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 25ce3d2fc38..bc76a68485d 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -347,9 +347,15 @@ export class CustomEditorContribution implements IWorkbenchContribution { options: ITextEditorOptions | undefined, group: IEditorGroup ): IOpenEditorOverride | undefined { + const userConfiguredEditors = this.customEditorService.getUserConfiguredCustomEditors(resource); + const contributedEditors = this.customEditorService.getContributedCustomEditors(resource); + if (!userConfiguredEditors.length && !contributedEditors.length) { + return; + } + // Check to see if there already an editor for the resource in the group. // If there is, we want to open that instead of creating a new editor. - // This ensures that we preserve whatever state the editor was previously in + // This ensures that we preserve whatever type of editor was previously being used // when the user switches back to it. const existingEditorForResource = group.editors.find(editor => isEqual(resource, editor.getResource())); if (existingEditorForResource) { @@ -358,14 +364,12 @@ export class CustomEditorContribution implements IWorkbenchContribution { }; } - const userConfiguredEditors = this.customEditorService.getUserConfiguredCustomEditors(resource); if (userConfiguredEditors.length) { return { override: this.customEditorService.openWith(resource, userConfiguredEditors.allEditors[0].id, options, group), }; } - const contributedEditors = this.customEditorService.getContributedCustomEditors(resource); if (!contributedEditors.length) { return; } From 904951e31f3279c8356cab225ceb4b44e9d1e3c8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 13 Jan 2020 20:18:56 -0800 Subject: [PATCH 233/843] Only return documentation code action in refactor menu --- .../contrib/codeActions/common/documentationContribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/codeActions/common/documentationContribution.ts b/src/vs/workbench/contrib/codeActions/common/documentationContribution.ts index cc26586e2b4..17cf4fa1d9f 100644 --- a/src/vs/workbench/contrib/codeActions/common/documentationContribution.ts +++ b/src/vs/workbench/contrib/codeActions/common/documentationContribution.ts @@ -57,7 +57,7 @@ export class CodeActionDocumentationContribution extends Disposable implements I } async provideCodeActions(_model: ITextModel, _range: Range | Selection, context: CodeActionContext, _token: CancellationToken): Promise { - if (!context.only || !CodeActionKind.Refactor.contains(new CodeActionKind(context.only))) { + if (CodeActionKind.Refactor.value !== context.only) { return { actions: [], dispose: () => { } From c044f090ce4398f717fff28689d2d45713166ee8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 13 Jan 2020 20:21:48 -0800 Subject: [PATCH 234/843] Remove unused vars --- src/vs/editor/contrib/codeAction/codeActionCommands.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 8185eca6b17..6c4912f4a93 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -16,21 +16,19 @@ import { IPosition } from 'vs/editor/common/core/position'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { CodeAction } from 'vs/editor/common/modes'; -import { CodeActionSet, refactorCommandId, sourceActionCommandId, codeActionCommandId, organizeImportsCommandId, fixAllCommandId } from 'vs/editor/contrib/codeAction/codeAction'; +import { codeActionCommandId, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionUi } from 'vs/editor/contrib/codeAction/codeActionUi'; import { MessageController } from 'vs/editor/contrib/message/messageController'; import * as nls from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { CodeActionModel, CodeActionsState, SUPPORTED_CODE_ACTIONS } from './codeActionModel'; -import { CodeActionAutoApply, CodeActionFilter, CodeActionKind, CodeActionTrigger, CodeActionCommandArgs } from './types'; +import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger } from './types'; function contextKeyForSupportedActions(kind: CodeActionKind) { return ContextKeyExpr.regex( @@ -83,8 +81,6 @@ export class QuickFixController extends Disposable implements IEditorContributio @IMarkerService markerService: IMarkerService, @IContextKeyService contextKeyService: IContextKeyService, @IEditorProgressService progressService: IEditorProgressService, - @IContextMenuService contextMenuService: IContextMenuService, - @IKeybindingService keybindingService: IKeybindingService, @ICommandService private readonly _commandService: ICommandService, @IBulkEditService private readonly _bulkEditService: IBulkEditService, @IInstantiationService private readonly _instantiationService: IInstantiationService, From 560f922d6f008b5aaac6dbbdfc7368128ed847c6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 13 Jan 2020 20:34:13 -0800 Subject: [PATCH 235/843] Add telemetry on which code actions are applied We are trying to improve the discoverability of code actions. To do this, we need to have a baseline of how often people are applying actions such as refactorings so that we can measure if changes like adding documentation have an impact on their usage rates. Ref #86788 --- .../contrib/codeAction/codeActionCommands.ts | 29 +++++++++++++++---- .../api/browser/mainThreadSaveParticipant.ts | 7 ++--- .../markers/browser/markersTreeViewer.ts | 6 +--- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 6c4912f4a93..ce30a8227fd 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -27,6 +27,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { IMarkerService } from 'vs/platform/markers/common/markers'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CodeActionModel, CodeActionsState, SUPPORTED_CODE_ACTIONS } from './codeActionModel'; import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger } from './types'; @@ -81,8 +82,6 @@ export class QuickFixController extends Disposable implements IEditorContributio @IMarkerService markerService: IMarkerService, @IContextKeyService contextKeyService: IContextKeyService, @IEditorProgressService progressService: IEditorProgressService, - @ICommandService private readonly _commandService: ICommandService, - @IBulkEditService private readonly _bulkEditService: IBulkEditService, @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { super(); @@ -133,21 +132,41 @@ export class QuickFixController extends Disposable implements IEditorContributio } private _applyCodeAction(action: CodeAction): Promise { - return this._instantiationService.invokeFunction(applyCodeAction, action, this._bulkEditService, this._commandService, this._editor); + return this._instantiationService.invokeFunction(applyCodeAction, action, this._editor); } } export async function applyCodeAction( accessor: ServicesAccessor, action: CodeAction, - bulkEditService: IBulkEditService, - commandService: ICommandService, editor?: ICodeEditor, ): Promise { + const bulkEditService = accessor.get(IBulkEditService); + const commandService = accessor.get(ICommandService); + const telemetryService = accessor.get(ITelemetryService); const notificationService = accessor.get(INotificationService); + + type ApplyCodeActionEvent = { + codeActionTitle: string; + codeActionKind: string | undefined; + codeActionIsPreferred: boolean; + }; + type ApplyCodeEventClassification = { + codeActionTitle: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + codeActionKind: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + codeActionIsPreferred: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + }; + + telemetryService.publicLog2('codeAction.applyCodeAction', { + codeActionTitle: action.title, + codeActionKind: action.kind, + codeActionIsPreferred: !!action.isPreferred, + }); + if (action.edit) { await bulkEditService.apply(action.edit, { editor }); } + if (action.command) { try { await commandService.executeCommand(action.command.id, ...(action.command.arguments || [])); diff --git a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts index 64d9ae97df7..764487cde6f 100644 --- a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts @@ -7,7 +7,6 @@ import { IdleValue } from 'vs/base/common/async'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import * as strings from 'vs/base/common/strings'; import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand'; import { EditOperation } from 'vs/editor/common/core/editOperation'; @@ -23,7 +22,7 @@ import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { localize } from 'vs/nls'; -import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; @@ -247,8 +246,6 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { class CodeActionOnSaveParticipant implements ISaveParticipantParticipant { constructor( - @IBulkEditService private readonly _bulkEditService: IBulkEditService, - @ICommandService private readonly _commandService: ICommandService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { } @@ -308,7 +305,7 @@ class CodeActionOnSaveParticipant implements ISaveParticipantParticipant { private async applyCodeActions(actionsToRun: readonly CodeAction[]) { for (const action of actionsToRun) { - await this._instantiationService.invokeFunction(applyCodeAction, action, this._bulkEditService, this._commandService); + await this._instantiationService.invokeFunction(applyCodeAction, action); } } diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 4b63f2f07b4..6563a93e622 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -39,8 +39,6 @@ import { Range } from 'vs/editor/common/core/range'; import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; import { ITextModel } from 'vs/editor/common/model'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; -import { ICommandService } from 'vs/platform/commands/common/commands'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; @@ -489,8 +487,6 @@ export class MarkerViewModel extends Disposable { private readonly marker: Marker, @IModelService private modelService: IModelService, @IInstantiationService private instantiationService: IInstantiationService, - @IBulkEditService private readonly bulkEditService: IBulkEditService, - @ICommandService private readonly commandService: ICommandService, @IEditorService private readonly editorService: IEditorService ) { super(); @@ -571,7 +567,7 @@ export class MarkerViewModel extends Disposable { true, () => { return this.openFileAtMarker(this.marker) - .then(() => this.instantiationService.invokeFunction(applyCodeAction, codeAction, this.bulkEditService, this.commandService)); + .then(() => this.instantiationService.invokeFunction(applyCodeAction, codeAction)); })); } From c3c54744ab61fa2a44569d00cf686601aad873bf Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 13 Jan 2020 20:55:32 -0800 Subject: [PATCH 236/843] Convert CodeActionTrigger type enum type --- .../editor/contrib/codeAction/codeAction.ts | 34 +++++++++---------- .../contrib/codeAction/codeActionCommands.ts | 7 ++-- .../contrib/codeAction/codeActionModel.ts | 12 +++---- .../editor/contrib/codeAction/codeActionUi.ts | 4 +-- .../codeAction/test/codeAction.test.ts | 20 +++++------ .../codeAction/test/codeActionModel.test.ts | 9 ++--- src/vs/editor/contrib/codeAction/types.ts | 7 +++- .../editor/contrib/hover/modesContentHover.ts | 4 +-- .../api/browser/mainThreadSaveParticipant.ts | 4 +-- .../markers/browser/markersTreeViewer.ts | 4 +-- .../api/extHostLanguageFeatures.test.ts | 9 ++--- 11 files changed, 60 insertions(+), 54 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index c4850175c63..37744867d8c 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -6,16 +6,16 @@ import { equals, flatten, isNonEmptyArray, mergeSort } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState'; import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; -import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTrigger as CodeActionTriggerKind } from 'vs/editor/common/modes'; +import * as modes from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './types'; -import { TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState'; -import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { CodeActionFilter, CodeActionKind, CodeActionTrigger, CodeActionTriggerType, filtersAction, mayIncludeActionsOfKind } from './types'; export const codeActionCommandId = 'editor.action.codeAction'; export const refactorCommandId = 'editor.action.refactor'; @@ -24,14 +24,14 @@ export const organizeImportsCommandId = 'editor.action.organizeImports'; export const fixAllCommandId = 'editor.action.fixAll'; export interface CodeActionSet extends IDisposable { - readonly validActions: readonly CodeAction[]; - readonly allActions: readonly CodeAction[]; + readonly validActions: readonly modes.CodeAction[]; + readonly allActions: readonly modes.CodeAction[]; readonly hasAutoFix: boolean; } class ManagedCodeActionSet extends Disposable implements CodeActionSet { - private static codeActionsComparator(a: CodeAction, b: CodeAction): number { + private static codeActionsComparator(a: modes.CodeAction, b: modes.CodeAction): number { if (isNonEmptyArray(a.diagnostics)) { if (isNonEmptyArray(b.diagnostics)) { return a.diagnostics[0].message.localeCompare(b.diagnostics[0].message); @@ -45,10 +45,10 @@ class ManagedCodeActionSet extends Disposable implements CodeActionSet { } } - public readonly validActions: readonly CodeAction[]; - public readonly allActions: readonly CodeAction[]; + public readonly validActions: readonly modes.CodeAction[]; + public readonly allActions: readonly modes.CodeAction[]; - public constructor(actions: readonly CodeAction[], disposables: DisposableStore) { + public constructor(actions: readonly modes.CodeAction[], disposables: DisposableStore) { super(); this._register(disposables); this.allActions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator); @@ -68,9 +68,9 @@ export function getCodeActions( ): Promise { const filter = trigger.filter || {}; - const codeActionContext: CodeActionContext = { + const codeActionContext: modes.CodeActionContext = { only: filter.include?.value, - trigger: trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic + trigger: trigger.type === CodeActionTriggerType.Manual ? modes.CodeActionTrigger.Manual : modes.CodeActionTrigger.Automatic }; const cts = new TextModelCancellationTokenSource(model, token); @@ -94,8 +94,8 @@ export function getCodeActions( } }); - const listener = CodeActionProviderRegistry.onDidChange(() => { - const newProviders = CodeActionProviderRegistry.all(model); + const listener = modes.CodeActionProviderRegistry.onDidChange(() => { + const newProviders = modes.CodeActionProviderRegistry.all(model); if (!equals(newProviders, providers)) { cts.cancel(); } @@ -114,7 +114,7 @@ function getCodeActionProviders( model: ITextModel, filter: CodeActionFilter ) { - return CodeActionProviderRegistry.all(model) + return modes.CodeActionProviderRegistry.all(model) // Don't include providers that we know will not return code actions of interest .filter(provider => { if (!provider.providedCodeActionKinds) { @@ -125,7 +125,7 @@ function getCodeActionProviders( }); } -registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise> { +registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise> { const { resource, rangeOrSelection, kind } = args; if (!(resource instanceof URI)) { throw illegalArgument(); @@ -149,7 +149,7 @@ registerLanguageCommand('_executeCodeActionProvider', async function (accessor, const codeActionSet = await getCodeActions( model, validatedRangeOrSelection, - { type: 'manual', filter: { includeSourceActions: true, include: kind && kind.value ? new CodeActionKind(kind.value) : undefined } }, + { type: CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include: kind && kind.value ? new CodeActionKind(kind.value) : undefined } }, CancellationToken.None); setTimeout(() => codeActionSet.dispose(), 100); diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index ce30a8227fd..003830553f6 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -29,7 +29,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CodeActionModel, CodeActionsState, SUPPORTED_CODE_ACTIONS } from './codeActionModel'; -import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger } from './types'; +import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger, CodeActionTriggerType } from './types'; function contextKeyForSupportedActions(kind: CodeActionKind) { return ContextKeyExpr.regex( @@ -97,7 +97,7 @@ export class QuickFixController extends Disposable implements IEditorContributio await this._applyCodeAction(action); } finally { if (retrigger) { - this._trigger({ type: 'auto', filter: {} }); + this._trigger({ type: CodeActionTriggerType.Auto, filter: {} }); } } } @@ -124,7 +124,7 @@ export class QuickFixController extends Disposable implements IEditorContributio MessageController.get(this._editor).closeMessage(); const triggerPosition = this._editor.getPosition(); - this._trigger({ type: 'manual', filter, autoApply, context: { notAvailableMessage, position: triggerPosition } }); + this._trigger({ type: CodeActionTriggerType.Manual, filter, autoApply, context: { notAvailableMessage, position: triggerPosition } }); } private _trigger(trigger: CodeActionTrigger) { @@ -176,7 +176,6 @@ export async function applyCodeAction( typeof message === 'string' ? message : nls.localize('applyCodeActionFailed', "An unknown error occurred while applying the code action")); - } } } diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index e5743489005..0f834c0e29f 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -16,7 +16,7 @@ import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/cont import { IMarkerService } from 'vs/platform/markers/common/markers'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { getCodeActions, CodeActionSet } from './codeAction'; -import { CodeActionTrigger } from './types'; +import { CodeActionTrigger, CodeActionTriggerType } from './types'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { isEqual } from 'vs/base/common/resources'; @@ -56,14 +56,14 @@ class CodeActionOracle extends Disposable { if (resources.some(resource => isEqual(resource, model.uri))) { this._autoTriggerTimer.cancelAndSet(() => { - this.trigger({ type: 'auto' }); + this.trigger({ type: CodeActionTriggerType.Auto }); }, this._delay); } } private _onCursorChange(): void { this._autoTriggerTimer.cancelAndSet(() => { - this.trigger({ type: 'auto' }); + this.trigger({ type: CodeActionTriggerType.Auto }); }, this._delay); } @@ -88,7 +88,7 @@ class CodeActionOracle extends Disposable { } const model = this._editor.getModel(); const selection = this._editor.getSelection(); - if (selection.isEmpty() && trigger.type === 'auto') { + if (selection.isEmpty() && trigger.type === CodeActionTriggerType.Auto) { const { lineNumber, column } = selection.getPosition(); const line = model.getLineContent(lineNumber); if (line.length === 0) { @@ -214,14 +214,14 @@ export class CodeActionModel extends Disposable { } const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, token)); - if (this._progressService && trigger.trigger.type === 'manual') { + if (this._progressService && trigger.trigger.type === CodeActionTriggerType.Manual) { this._progressService.showWhile(actions, 250); } this.setState(new CodeActionsState.Triggered(trigger.trigger, trigger.selection, trigger.position, actions)); }, undefined); - this._codeActionOracle.value.trigger({ type: 'auto' }); + this._codeActionOracle.value.trigger({ type: CodeActionTriggerType.Auto }); } else { this._supportedCodeActions.reset(); } diff --git a/src/vs/editor/contrib/codeAction/codeActionUi.ts b/src/vs/editor/contrib/codeAction/codeActionUi.ts index 5c34b32fe7f..6f743d1d76e 100644 --- a/src/vs/editor/contrib/codeAction/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/codeActionUi.ts @@ -16,7 +16,7 @@ import { MessageController } from 'vs/editor/contrib/message/messageController'; import { CodeActionsState } from './codeActionModel'; import { CodeActionMenu, CodeActionShowOptions } from './codeActionMenu'; import { LightBulbWidget } from './lightBulbWidget'; -import { CodeActionAutoApply, CodeActionTrigger } from './types'; +import { CodeActionAutoApply, CodeActionTrigger, CodeActionTriggerType } from './types'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class CodeActionUi extends Disposable { @@ -67,7 +67,7 @@ export class CodeActionUi extends Disposable { this._lightBulbWidget.getValue().update(actions, newState.position); - if (newState.trigger.type === 'manual') { + if (newState.trigger.type === CodeActionTriggerType.Manual) { if (newState.trigger.filter?.include) { // Triggered for specific scope // Check to see if we want to auto apply. diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index 0b7fb8ba4a6..bd12d41230d 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { TextModel } from 'vs/editor/common/model/textModel'; import * as modes from 'vs/editor/common/modes'; import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; -import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; +import { CodeActionKind, CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -125,7 +125,7 @@ suite('CodeAction', () => { testData.tsLint.abc ]; - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'manual' }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Manual }, CancellationToken.None); assert.equal(actions.length, 6); assert.deepEqual(actions, expected); }); @@ -140,20 +140,20 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None); assert.equal(actions.length, 2); assert.strictEqual(actions[0].title, 'a'); assert.strictEqual(actions[1].title, 'a.b'); } { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a.b'); } { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None); assert.equal(actions.length, 0); } }); @@ -172,7 +172,7 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a'); }); @@ -186,13 +186,13 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto' }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'b'); } { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a'); } @@ -209,7 +209,7 @@ suite('CodeAction', () => { { const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { - type: 'auto', filter: { + type: CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source.append('test'), excludes: [CodeActionKind.Source], includeSourceActions: true, @@ -234,7 +234,7 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { - type: 'auto', + type: CodeActionTriggerType.Auto, filter: { include: CodeActionKind.QuickFix } diff --git a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts index c4a7cba5173..d77f4010da3 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts @@ -15,6 +15,7 @@ import { CodeActionModel, CodeActionsState } from 'vs/editor/contrib/codeAction/ import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types'; const testProvider = { provideCodeActions(): modes.CodeActionList { @@ -59,7 +60,7 @@ suite('CodeActionModel', () => { disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { assertType(e.type === CodeActionsState.Type.Triggered); - assert.strictEqual(e.trigger.type, 'auto'); + assert.strictEqual(e.trigger.type, CodeActionTriggerType.Auto); assert.ok(e.actions); e.actions.then(fixes => { @@ -100,7 +101,7 @@ suite('CodeActionModel', () => { disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { assertType(e.type === CodeActionsState.Type.Triggered); - assert.equal(e.trigger.type, 'auto'); + assert.equal(e.trigger.type, CodeActionTriggerType.Auto); assert.ok(e.actions); e.actions.then(fixes => { model.dispose(); @@ -138,7 +139,7 @@ suite('CodeActionModel', () => { disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { assertType(e.type === CodeActionsState.Type.Triggered); - assert.equal(e.trigger.type, 'auto'); + assert.equal(e.trigger.type, CodeActionTriggerType.Auto); const selection = e.rangeOrSelection; assert.deepEqual(selection.selectionStartLineNumber, 1); assert.deepEqual(selection.selectionStartColumn, 1); @@ -163,7 +164,7 @@ suite('CodeActionModel', () => { disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { assertType(e.type === CodeActionsState.Type.Triggered); - assert.equal(e.trigger.type, 'auto'); + assert.equal(e.trigger.type, CodeActionTriggerType.Auto); ++triggerCount; // give time for second trigger before completing test diff --git a/src/vs/editor/contrib/codeAction/types.ts b/src/vs/editor/contrib/codeAction/types.ts index bce9f612329..606d9b8eced 100644 --- a/src/vs/editor/contrib/codeAction/types.ts +++ b/src/vs/editor/contrib/codeAction/types.ts @@ -102,8 +102,13 @@ export function filtersAction(filter: CodeActionFilter, action: CodeAction): boo return true; } +export const enum CodeActionTriggerType { + Auto, + Manual +} + export interface CodeActionTrigger { - readonly type: 'auto' | 'manual'; + readonly type: CodeActionTriggerType; readonly filter?: CodeActionFilter; readonly autoApply?: CodeActionAutoApply; readonly context?: { diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 63fd6053301..95b8e9813c8 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -34,7 +34,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; import { QuickFixAction, QuickFixController } from 'vs/editor/contrib/codeAction/codeActionCommands'; -import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; +import { CodeActionKind, CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -592,7 +592,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { return getCodeActions( this._editor.getModel()!, new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn), - { type: 'manual', filter: { include: CodeActionKind.QuickFix } }, + { type: CodeActionTriggerType.Manual, filter: { include: CodeActionKind.QuickFix } }, cancellationToken); }); } diff --git a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts index 764487cde6f..667865985c1 100644 --- a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts @@ -18,7 +18,7 @@ import { CodeAction } from 'vs/editor/common/modes'; import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; -import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; +import { CodeActionKind, CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types'; import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { localize } from 'vs/nls'; @@ -311,7 +311,7 @@ class CodeActionOnSaveParticipant implements ISaveParticipantParticipant { private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], token: CancellationToken) { return getCodeActions(model, model.getFullModelRange(), { - type: 'auto', + type: CodeActionTriggerType.Auto, filter: { include: codeActionKind, excludes: excludes, includeSourceActions: true }, }, token); } diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 6563a93e622..d7d54627f04 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -37,7 +37,7 @@ import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/com import { IModelService } from 'vs/editor/common/services/modelService'; import { Range } from 'vs/editor/common/core/range'; import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; -import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; +import { CodeActionKind, CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; @@ -548,7 +548,7 @@ export class MarkerViewModel extends Disposable { if (model) { if (!this.codeActionsPromise) { this.codeActionsPromise = createCancelablePromise(cancellationToken => { - return getCodeActions(model, new Range(this.marker.range.startLineNumber, this.marker.range.startColumn, this.marker.range.endLineNumber, this.marker.range.endColumn), { type: 'manual', filter: { include: CodeActionKind.QuickFix } }, cancellationToken).then(actions => { + return getCodeActions(model, new Range(this.marker.range.startLineNumber, this.marker.range.startColumn, this.marker.range.endLineNumber, this.marker.range.endColumn), { type: CodeActionTriggerType.Manual, filter: { include: CodeActionKind.QuickFix } }, cancellationToken).then(actions => { return this._register(actions); }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index 232becb2a56..8e6fb02412c 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -47,6 +47,7 @@ import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { dispose } from 'vs/base/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = EditorModel.createFromString( @@ -589,7 +590,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Manual }, CancellationToken.None); assert.equal(actions.length, 2); const [first, second] = actions; assert.equal(first.title, 'Testing1'); @@ -613,7 +614,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Manual }, CancellationToken.None); assert.equal(actions.length, 1); const [first] = actions; assert.equal(first.title, 'Testing1'); @@ -636,7 +637,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Manual }, CancellationToken.None); assert.equal(actions.length, 1); }); @@ -654,7 +655,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Manual }, CancellationToken.None); assert.equal(actions.length, 1); }); From 501085f224933a62bdabc2e2248f8770f33fa7cb Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 13 Jan 2020 21:51:16 -0800 Subject: [PATCH 237/843] Add basic save functionality to search editor --- .../search/browser/search.contribution.ts | 5 +- .../contrib/search/browser/searchEditor.ts | 21 ++- .../search/browser/searchEditorCommands.ts | 121 ++++++++++++------ 3 files changed, 107 insertions(+), 40 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 3e698a8a74c..9e4d6ce61d8 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -57,7 +57,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { assertType } from 'vs/base/common/types'; import { SearchViewPaneContainer } from 'vs/workbench/contrib/search/browser/searchViewlet'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; -import { SearchEditorInput, SearchEditorInputFactory } from 'vs/workbench/contrib/search/browser/searchEditorCommands'; +import { SearchEditorInput, SearchEditorInputFactory, SearchEditorContribution } from 'vs/workbench/contrib/search/browser/searchEditorCommands'; import { SearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; @@ -909,3 +909,6 @@ Registry.as(EditorExtensions.Editors).registerEditor( Registry.as(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory( SearchEditorInput.ID, SearchEditorInputFactory); + +const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchContributionsRegistry.registerWorkbenchContribution(SearchEditorContribution, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/search/browser/searchEditor.ts b/src/vs/workbench/contrib/search/browser/searchEditor.ts index 505ea541890..26bc97814d1 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditor.ts @@ -158,6 +158,8 @@ export class SearchEditor extends BaseEditor { } } }); + + this._register(this.searchResultEditor.onDidChangeModel(() => this.hideHeader())); } private async runSearch(instant = false) { @@ -225,14 +227,28 @@ export class SearchEditor extends BaseEditor { (assertIsDefined(this._input) as SearchEditorInput).setConfig(config); const labelFormatter = (uri: URI): string => this.labelService.getUriLabel(uri, { relative: true }); - const results = serializeSearchResultForEditor(searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, false); + const results = serializeSearchResultForEditor(searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, true); const textModel = assertIsDefined(this.searchResultEditor.getModel()); textModel.setValue(results.text.join(lineDelimiter)); + this.hideHeader(); textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); searchModel.dispose(); } + private hideHeader() { + const headerLines = + this.searchResultEditor + .getModel() + ?.getValueInRange(new Range(1, 1, 6, 1)) + .split('\n') + .filter(line => line.startsWith('#')) + .length + ?? 0; + + this.searchResultEditor.setHiddenAreas([new Range(1, 1, headerLines + 1, 1)]); + } + layout(dimension: DOM.Dimension) { this.dimension = dimension; this.reLayout(); @@ -255,8 +271,7 @@ export class SearchEditor extends BaseEditor { if (!(newInput instanceof SearchEditorInput)) { return; } this.pauseSearching = true; // TODO: Manage model lifecycle in SearchEditorInput - const model = this.modelService.getModel(newInput.getResource()); - + const model = this.modelService.getModel(newInput.resource); this.searchResultEditor.setModel(model); this.queryEditorWidget.setValue(newInput.config.query, true); this.queryEditorWidget.searchInput.setCaseSensitive(newInput.config.caseSensitive); diff --git a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts index 07177d95ea8..c0caeedac62 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts @@ -5,13 +5,13 @@ import { coalesce, flatten } from 'vs/base/common/arrays'; import * as network from 'vs/base/common/network'; -import { repeat } from 'vs/base/common/strings'; +import { repeat, endsWith } from 'vs/base/common/strings'; import { assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/searchEditor'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; -import { EndOfLinePreference, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { EndOfLinePreference, TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -25,16 +25,22 @@ import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/co import { FileMatch, Match, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPatternInfo, ISearchConfigurationProperties, ITextQuery } from 'vs/workbench/services/search/common/search'; -import { EditorInput, IEditorInputFactory } from 'vs/workbench/common/editor'; +import { IEditorInputFactory, GroupIdentifier, EditorInput } from 'vs/workbench/common/editor'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { SearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; +import type { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +// import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; + export type SearchConfiguration = { query: string, includes: string, - excludes: string, + excludes: string contextLines: number, wholeWord: boolean, caseSensitive: boolean, @@ -43,6 +49,27 @@ export type SearchConfiguration = { showIncludesExcludes: boolean, }; +export class SearchEditorContribution implements IWorkbenchContribution { + // constructor( + // @IEditorService private readonly editorService: IEditorService, + // @ITextFileService protected readonly textFileService: ITextFileService, + // ) { + // this.editorService.overrideOpenEditor(async (editor, options, group) => { + // const resource = editor.getResource(); + // if (!resource || !endsWith(resource.path, '.code-search') || !(editor instanceof FileEditorInput)) { + // return undefined; + // } + + // console.log('hello agian'); + // const contents = await this.textFileService.read(resource); + // contents.value; + + // return undefined; + + // }); + // } +} + export class SearchEditorInputFactory implements IEditorInputFactory { canSerialize() { return true; } @@ -52,7 +79,7 @@ export class SearchEditorInputFactory implements IEditorInputFactory { } deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): SearchEditorInput | undefined { - return instantiationService.createInstance(SearchEditorInput, JSON.parse(serializedEditorInput)); + return instantiationService.createInstance(SearchEditorInput, JSON.parse(serializedEditorInput), undefined); } } @@ -60,40 +87,56 @@ let searchEditorInputInstances = 0; export class SearchEditorInput extends EditorInput { static readonly ID: string = 'workbench.editorinputs.searchEditorInput'; - public config: SearchConfiguration; - private instanceNumber: number = searchEditorInputInstances++; + private _config: SearchConfiguration; + public get config(): Readonly { + return this._config; + } + + private model: ITextModel; + public readonly resource: URI; constructor( config: SearchConfiguration | undefined, + initialContents: string | undefined, @IModelService private readonly modelService: IModelService, @IModeService private readonly modeService: IModeService, + @IEditorService protected readonly editorService: IEditorService, + @IEditorGroupsService protected readonly editorGroupService: IEditorGroupsService, + @ITextFileService protected readonly textFileService: ITextFileService, + @IUntitledTextEditorService protected readonly untitledTextEditorService: IUntitledTextEditorService, ) { super(); + this.resource = URI.from({ scheme: 'search-editor', fragment: `${searchEditorInputInstances++}` }); + if (config === undefined) { - this.config = { query: '', includes: '', excludes: '', contextLines: 0, wholeWord: false, caseSensitive: false, regexp: false, useIgnores: true, showIncludesExcludes: false }; + this._config = { query: '', includes: '', excludes: '', contextLines: 0, wholeWord: false, caseSensitive: false, regexp: false, useIgnores: true, showIncludesExcludes: false }; } else { - this.config = config; + this._config = config; } const searchResultMode = this.modeService.create('search-result'); - this.modelService.createModel('', searchResultMode, this.getResource()); + + this.model = this.modelService.createModel(initialContents ?? '', searchResultMode, this.resource); + } + + async save(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { + const path = (this.config.query.replace(/\W/g, '') || 'Untitled Search') + '.code-search'; + const untitledResource = URI.from({ scheme: 'untitled', path, fragment: this.resource.fragment }); + const a = this.untitledTextEditorService.createOrGet(untitledResource, 'search-result', this.model.getValue()); + return a.save(group, options).finally(() => a.dispose()); } getTypeId(): string { return SearchEditorInput.ID; } - getResource(): URI { - return URI.from({ scheme: 'search-editor', fragment: `${this.instanceNumber}` }); - } - getName(): string { return this.config.query ? localize('searchTitle.withQuery', "Search: {0}", this.config.query) : localize('searchTitle', "Search"); } setConfig(config: SearchConfiguration) { - this.config = config; + this._config = config; this._onDidChangeLabel.fire(); } @@ -102,9 +145,17 @@ export class SearchEditorInput extends EditorInput { } dispose() { - this.modelService.destroyModel(this.getResource()); + this.modelService.destroyModel(this.resource); super.dispose(); } + + matches(other: unknown) { + if (this === other) { return true; } + if (other instanceof UntitledTextEditorInput) { + if (other.getResource().fragment === this.resource.fragment) { return true; } + } + return false; + } } // Using \r\n on Windows inserts an extra newline between results. @@ -313,7 +364,7 @@ export const serializeSearchResultForEditor = (searchResult: SearchResult, rawIn .map(folderMatch => folderMatch.matches().sort(searchMatchComparer) .map(fileMatch => fileMatchToSearchResultFormat(fileMatch, labelFormatter))))); - return { matchRanges: allResults.matchRanges.map(translateRangeLines(header.length)), text: header.concat(allResults.text) }; + return { matchRanges: allResults.matchRanges.map(translateRangeLines(header.length)), text: header.concat(allResults.text.length ? allResults.text : ['No Results']) }; }; export const refreshActiveEditorSearch = @@ -373,7 +424,7 @@ export const refreshActiveEditorSearch = await searchModel.search(query); const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true }); - const results = serializeSearchResultForEditor(searchModel.searchResult, contentPattern.includes, contentPattern.excludes, contextLines, labelFormatter, false); + const results = serializeSearchResultForEditor(searchModel.searchResult, contentPattern.includes, contentPattern.excludes, contextLines, labelFormatter, true); textModel.setValue(results.text.join(lineDelimiter)); textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); @@ -381,7 +432,7 @@ export const refreshActiveEditorSearch = export const openNewSearchEditor = async (editorService: IEditorService, instantiationService: IInstantiationService) => { - await editorService.openEditor(instantiationService.createInstance(SearchEditorInput, undefined), { pinned: true }); + await editorService.openEditor(instantiationService.createInstance(SearchEditorInput, undefined, undefined), { pinned: true }); }; export const createEditorFromSearchResult = @@ -395,7 +446,7 @@ export const createEditorFromSearchResult = const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true }); - const results = serializeSearchResultForEditor(searchResult, rawIncludePattern, rawExcludePattern, 0, labelFormatter, false); + const results = serializeSearchResultForEditor(searchResult, rawIncludePattern, rawExcludePattern, 0, labelFormatter, true); const contents = results.text.join(lineDelimiter); let possible = { contents, @@ -418,24 +469,22 @@ export const createEditorFromSearchResult = existing = editorService.getOpened(possible); } - const editor = await editorService.openEditor( - instantiationService.createInstance( - SearchEditorInput, - { - query: searchResult.query.contentPattern.pattern, - regexp: !!searchResult.query.contentPattern.isRegExp, - caseSensitive: !!searchResult.query.contentPattern.isCaseSensitive, - wholeWord: !!searchResult.query.contentPattern.isWordMatch, - includes: rawIncludePattern, - excludes: rawExcludePattern, - contextLines: 0, - useIgnores: !searchResult.query.userDisabledExcludesAndIgnoreFiles, - showIncludesExcludes: !!(rawExcludePattern || rawExcludePattern || searchResult.query.userDisabledExcludesAndIgnoreFiles) - }), - { pinned: true }) as SearchEditor; + const input = instantiationService.createInstance( + SearchEditorInput, + { + query: searchResult.query.contentPattern.pattern, + regexp: !!searchResult.query.contentPattern.isRegExp, + caseSensitive: !!searchResult.query.contentPattern.isCaseSensitive, + wholeWord: !!searchResult.query.contentPattern.isWordMatch, + includes: rawIncludePattern, + excludes: rawExcludePattern, + contextLines: 0, + useIgnores: !searchResult.query.userDisabledExcludesAndIgnoreFiles, + showIncludesExcludes: !!(rawExcludePattern || rawExcludePattern || searchResult.query.userDisabledExcludesAndIgnoreFiles) + }, contents); + const editor = await editorService.openEditor(input, { pinned: true }) as SearchEditor; const model = assertIsDefined(editor.getModel()); - model.setValue(contents); model.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); }; From bf350ef870f229b334d3f54a7cc81a7e73841c3b Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 13 Jan 2020 22:32:17 -0800 Subject: [PATCH 238/843] remove unused --- src/vs/workbench/contrib/search/browser/searchEditorCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts index c0caeedac62..985f49c4347 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts @@ -5,7 +5,7 @@ import { coalesce, flatten } from 'vs/base/common/arrays'; import * as network from 'vs/base/common/network'; -import { repeat, endsWith } from 'vs/base/common/strings'; +import { repeat } from 'vs/base/common/strings'; import { assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/searchEditor'; From 52702da6ef7e381ac3a6cc0f1b25747a007b9401 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jan 2020 08:34:31 +0100 Subject: [PATCH 239/843] Same editor appears twice in tabs (fix #88551) --- src/vs/workbench/browser/parts/editor/textDiffEditor.ts | 5 +++-- src/vs/workbench/common/editor/diffEditorInput.ts | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 66963c29490..3dbb5f41ac5 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -26,7 +26,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/editor'; @@ -182,7 +182,8 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { options = EditorOptions.create(preservingOptions); } - this.editorService.openEditor(binaryDiffInput, options, this.group); + // Replace this editor with the binary one + this.editorService.replaceEditors([{ editor: input, replacement: binaryDiffInput, options }], this.group || ACTIVE_GROUP); return true; } diff --git a/src/vs/workbench/common/editor/diffEditorInput.ts b/src/vs/workbench/common/editor/diffEditorInput.ts index 12e57ac49e0..b9f1bc2811c 100644 --- a/src/vs/workbench/common/editor/diffEditorInput.ts +++ b/src/vs/workbench/common/editor/diffEditorInput.ts @@ -32,6 +32,7 @@ export class DiffEditorInput extends SideBySideEditorInput { if (!super.matches(otherInput)) { return false; } + return otherInput instanceof DiffEditorInput && otherInput.forceOpenAsBinary === this.forceOpenAsBinary; } From 7a62151d6a966be9d9ed4ea659cbd7a4c631c960 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 14 Jan 2020 09:26:42 +0100 Subject: [PATCH 240/843] fix https://github.com/microsoft/vscode/issues/88591 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 57808f0f833..ef153437878 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -31,7 +31,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.44", + "version": "0.0.45", "repo": "https://github.com/Microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", From 980d8ef1371b14e80162d88e88f45e15f8632070 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 14 Jan 2020 09:36:59 +0100 Subject: [PATCH 241/843] #87461 Fix open view. Consider existing views. --- src/vs/workbench/browser/parts/views/views.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index b2911ed6663..3ba413e80ef 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -649,6 +649,7 @@ export class ViewsService extends Disposable implements IViewsService { this.viewDisposable.clear(); })); + this.viewContainersRegistry.all.forEach(viewContainer => this.onViewsRegistered(this.viewDescriptorService.getViews(viewContainer), viewContainer)); this._register(this.viewDescriptorService.onViewsRegistered(({ views, viewContainer }) => this.onViewsRegistered(views, viewContainer))); this._register(this.viewDescriptorService.onViewsDeregistered(({ views, viewContainer }) => this.onViewsDeregistered(views, viewContainer))); } From ad1500b9db68fd8d6d15e583e2897b9e1af8f5b2 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 14 Jan 2020 10:03:25 +0100 Subject: [PATCH 242/843] Extract paste selection clipboard code to an action --- .../electron-browser/selectionClipboard.ts | 65 +++++++++++-------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts b/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts index c6898134ef7..d93deb4a700 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { registerEditorContribution, EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { Range } from 'vs/editor/common/core/range'; @@ -19,8 +20,10 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class SelectionClipboard extends Disposable implements IEditorContribution { private static readonly SELECTION_LENGTH_LIMIT = 65536; @@ -81,30 +84,6 @@ export class SelectionClipboard extends Disposable implements IEditorContributio } setSelectionToClipboard.schedule(); })); - - this._register(editor.onKeyDown((e: IKeyboardEvent) => { - if (!isEnabled) { - return; - } - - if (!editor.hasModel()) { - return; - } - - if (e.equals(KeyMod.Shift | KeyCode.Insert)) { - // prevent paste from 'clipboard' clipboard - e.preventDefault(); - - // trigger paste from 'primary' clipboard - clipboardService.readText('selection').then(text => { - editor.focus(); - editor.trigger('keyboard', Handler.Paste, { - text: text, - pasteOnNewLine: false - }); - }); - } - })); } } @@ -133,5 +112,39 @@ class SelectionClipboardPastePreventer implements IWorkbenchContribution { } } +class PasteSelectionClipboardAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.selectionClipboardPaste', + label: nls.localize('actions.pasteSelectionClipboard', "Paste Selection Clipboard"), + alias: 'Paste Selection Clipboard', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: ContextKeyExpr.and( + EditorContextKeys.editorTextFocus, + ContextKeyExpr.has('config.editor.selectionClipboard') + ), + primary: KeyMod.Shift | KeyCode.Insert, + weight: KeybindingWeight.EditorContrib + } + }); + } + + public async run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): Promise { + const clipboardService = accessor.get(IClipboardService); + + // read selection clipboard + const text = await clipboardService.readText('selection'); + + editor.trigger('keyboard', Handler.Paste, { + text: text, + pasteOnNewLine: false, + multicursorText: null + }); + } +} + registerEditorContribution(SelectionClipboardContributionID, SelectionClipboard); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SelectionClipboardPastePreventer, LifecyclePhase.Ready); +registerEditorAction(PasteSelectionClipboardAction); From fecfa6ecdb5a91e57e3c48f75f2211fb8f8e583b Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Tue, 14 Jan 2020 10:06:18 +0100 Subject: [PATCH 243/843] Fix css --- src/vs/editor/contrib/suggest/media/suggest.css | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index e001eb32bff..5efde04b9ae 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -167,11 +167,16 @@ display: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { +.monaco-editor .suggest-widget.always-reveal-inline-details .monaco-list .monaco-list-row > .contents > .main > .readMore, +.monaco-editor .suggest-widget.always-reveal-inline-details.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, +.monaco-editor .suggest-widget.always-reveal-inline-details.docs-below .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { display: inline; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.focused) > .contents > .main > .readMore { - display: inline; + +.monaco-editor .suggest-widget.always-reveal-inline-details .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { + visibility: visible; +} +.monaco-editor .suggest-widget.always-reveal-inline-details .monaco-list .monaco-list-row:not(.focused) > .contents > .main > .readMore { visibility: hidden; } From a4a3e90dc8ca32d8a47d4a659a81f4cecf11082f Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 14 Jan 2020 10:07:08 +0100 Subject: [PATCH 244/843] Only register PasteSelectionClipboardAction on Linux --- .../contrib/codeEditor/electron-browser/selectionClipboard.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts b/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts index d93deb4a700..536d0f75c5c 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts @@ -147,4 +147,6 @@ class PasteSelectionClipboardAction extends EditorAction { registerEditorContribution(SelectionClipboardContributionID, SelectionClipboard); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SelectionClipboardPastePreventer, LifecyclePhase.Ready); -registerEditorAction(PasteSelectionClipboardAction); +if (platform.isLinux) { + registerEditorAction(PasteSelectionClipboardAction); +} From 3140b9c99f32731a79a0e609fa92d072b4a96cca Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 14 Jan 2020 10:07:29 +0100 Subject: [PATCH 245/843] fix #88561 --- .../bulkEdit/browser/bulkEdit.contribution.ts | 11 ++- .../contrib/bulkEdit/browser/bulkEditPane.ts | 69 ++++++++++++------- .../bulkEdit/browser/bulkEditPreview.ts | 12 ++-- 3 files changed, 62 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index 2a705730bae..104ed75e937 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -22,6 +22,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { URI } from 'vs/base/common/uri'; function getBulkEditPane(panelService: IPanelService): BulkEditPane | undefined { let view: ViewPane | undefined; @@ -84,7 +85,15 @@ class BulkEditPreviewContribution { // (3) close preview editors for (let group of this._editorGroupsService.groups) { for (let input of group.editors) { - if (input instanceof DiffEditorInput && input.modifiedInput.getResource()?.scheme === BulkEditPreviewProvider.Schema) { + + let resource: URI | undefined; + if (input instanceof DiffEditorInput) { + resource = input.modifiedInput.getResource(); + } else { + resource = input.getResource(); + } + + if (resource?.scheme === BulkEditPreviewProvider.Schema) { group.closeEditor(input, { preserveFocus: true }); } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index d0310fcc727..982e5704712 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -15,7 +15,7 @@ import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistr import { localize } from 'vs/nls'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { BulkEditPreviewProvider, BulkFileOperations } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; +import { BulkEditPreviewProvider, BulkFileOperations, BulkTextEdit, BulkFileOperationType } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { URI } from 'vs/base/common/uri'; @@ -28,6 +28,7 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; +import { basename } from 'vs/base/common/resources'; const enum State { Data = 'data', @@ -107,9 +108,9 @@ export class BulkEditPane extends ViewPane { this._disposables.add(this._tree.onDidOpen(e => { const [first] = e.elements; if (first instanceof TextEditElement) { - this._previewTextEditElement(first); + this._openElementAsEditor(first.parent, first.edit); } else if (first instanceof FileElement) { - this._previewFileElement(); + this._openElementAsEditor(first); } })); @@ -214,32 +215,54 @@ export class BulkEditPane extends ViewPane { this._sessionDisposables.clear(); } - private async _previewTextEditElement(element: TextEditElement): Promise { + private async _openElementAsEditor(fileElement: FileElement, textElement?: BulkTextEdit): Promise { - let leftResource: URI; - try { - (await this._textModelService.createModelReference(element.parent.uri)).dispose(); - leftResource = element.parent.uri; - } catch { - leftResource = BulkEditPreviewProvider.emptyPreview; + if (!textElement) { + textElement = fileElement.edit.textEdits[0]; } - const previewUri = BulkEditPreviewProvider.asPreviewUri(element.parent.uri); - - this._editorService.openEditor({ - leftResource, - rightResource: previewUri, - label: localize('edt.title', "{0} (Refactor Preview)", this._labelService.getUriLabel(element.parent.uri)), - options: { - selection: element.edit.edit.range, - revealInCenterIfOutsideViewport: true, - preserveFocus: true + let leftResource: URI | undefined; + if (fileElement.edit.type & BulkFileOperationType.TextEdit) { + try { + (await this._textModelService.createModelReference(fileElement.uri)).dispose(); + leftResource = fileElement.uri; + } catch { + leftResource = BulkEditPreviewProvider.emptyPreview; } - }); - } + } - private _previewFileElement(): void { + const previewUri = BulkEditPreviewProvider.asPreviewUri(fileElement.uri); + if (leftResource) { + // show diff editor + this._editorService.openEditor({ + leftResource, + rightResource: previewUri, + label: localize('edt.title', "{0} (refactor preview)", basename(fileElement.uri)), + options: { + selection: textElement?.edit.range, + revealInCenterIfOutsideViewport: true, + preserveFocus: true + } + }); + } else { + // show 'normal' editor + + let typeLabel: string | undefined; + if (fileElement.edit.type & BulkFileOperationType.Rename) { + typeLabel = localize('rename', "rename"); + } else if (fileElement.edit.type & BulkFileOperationType.Create) { + typeLabel = localize('create', "create"); + } else if (fileElement.edit.type & BulkFileOperationType.Delete) { + typeLabel = localize('delete', "delete"); + } + + this._editorService.openEditor({ + label: typeLabel && localize('edt.title2', "{0} ({1}, refactor preview)", basename(fileElement.uri), typeLabel), + resource: previewUri, + options: { preserveFocus: true } + }); + } } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index 614c152f94a..6f9ae847448 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -49,15 +49,15 @@ export class BulkTextEdit extends CheckedObject { } export const enum BulkFileOperationType { - None = 0, - Create = 0b0001, - Delete = 0b0010, - Rename = 0b0100, + TextEdit = 1, + Create = 2, + Delete = 4, + Rename = 8, } export class BulkFileOperation extends CheckedObject { - type = BulkFileOperationType.None; + type: BulkFileOperationType = 0; textEdits: BulkTextEdit[] = []; originalEdits = new Map(); newUri?: URI; @@ -118,7 +118,7 @@ export class BulkFileOperations { let type: BulkFileOperationType; if (isResourceTextEdit(edit)) { - type = BulkFileOperationType.None; + type = BulkFileOperationType.TextEdit; uri = edit.resource; } else if (edit.newUri && edit.oldUri) { From 5ee9f6646e5d813989d886326c9c5b6f381814bb Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 14 Jan 2020 10:07:40 +0100 Subject: [PATCH 246/843] add constants --- .../typescript-language-features/package.json | 2 +- .../src/features/semanticTokens.ts | 108 ++++++++++-------- .../typescript-language-features/yarn.lock | 8 +- 3 files changed, 68 insertions(+), 50 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 3ff2f98b8e4..681c5c4b2d1 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -19,7 +19,7 @@ "jsonc-parser": "^2.1.1", "rimraf": "^2.6.3", "semver": "5.5.1", - "typescript-vscode-sh-plugin": "^0.4.1", + "typescript-vscode-sh-plugin": "^0.3.2", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0" }, diff --git a/extensions/typescript-language-features/src/features/semanticTokens.ts b/extensions/typescript-language-features/src/features/semanticTokens.ts index de3f11e81d7..78f7dfb36a8 100644 --- a/extensions/typescript-language-features/src/features/semanticTokens.ts +++ b/extensions/typescript-language-features/src/features/semanticTokens.ts @@ -16,8 +16,6 @@ export function register(selector: vscode.DocumentSelector, client: ITypeScriptS const provider = new SemanticTokensProvider(client); return vscode.languages.registerSemanticTokensProvider(selector, provider, provider.getLegend()); }); - const provider = new SemanticTokensProvider(client); - return vscode.languages.registerSemanticTokensProvider(selector, provider, provider.getLegend()); } /** @@ -32,12 +30,12 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider { getLegend(): vscode.SemanticTokensLegend { const tokenTypes = []; - for (let i = 0; i < TokenType._sentinel; i++) { - tokenTypes.push(TokenType[i]); + for (let i = 0; i < VSCodeShPlugin.TokenType._sentinel; i++) { + tokenTypes.push(VSCodeShPlugin.TokenType[i]); } const tokenModifiers = []; - for (let i = 0; i < TokenModifier._sentinel; i++) { - tokenModifiers.push(TokenModifier[i]); + for (let i = 0; i < VSCodeShPlugin.TokenModifier._sentinel; i++) { + tokenModifiers.push(VSCodeShPlugin.TokenModifier[i]); } return new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers); } @@ -76,25 +74,25 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider { const builder = new vscode.SemanticTokensBuilder(); for (const tokenSpan of allTokenSpans) { - for (let i = 0, len = Math.floor(tokenSpan.length / 3); i < len; i++) { + let i = 0; + while (i < tokenSpan.length) { + const offset = tokenSpan[i++]; + const length = tokenSpan[i++]; + const tsClassification = tokenSpan[i++]; - const tsClassification = tokenSpan[3 * i + 2]; - let tokenType = 0; let tokenModifiers = 0; - if (tsClassification >= 0x100) { - // exendend classifications as returned by the typescript-vscode-sh-plugin - tokenType = (tsClassification >> 8) - 1; - tokenModifiers = tsClassification & 0xFF; + let tokenType = VSCodeShPlugin.getTokenTypeFromClassification(tsClassification); + if (tokenType !== undefined) { + // it's a classification as returned by the typescript-vscode-sh-plugin + tokenModifiers = VSCodeShPlugin.getTokenModifierFromClassification(tsClassification); } else { + // typescript-vscode-sh-plugin is not present tokenType = tokenTypeMap[tsClassification]; if (tokenType === undefined) { continue; } } - const offset = tokenSpan[3 * i]; - const length = tokenSpan[3 * i + 1]; - // we can use the document's range conversion methods because the result is at the same version as the document const startPos = document.positionAt(offset); const endPos = document.positionAt(offset + length); @@ -110,41 +108,61 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider { } } -// Don't change TokenType and TokenModifier enums without adopting typescript-vscode-sh-plugin -enum TokenType { - 'class', - 'enum', - 'interface', - 'namespace', - 'typeParameter', - 'type', - 'parameter', - 'variable', - 'property', - 'constant', - 'function', - 'member', - _sentinel -} +namespace VSCodeShPlugin { -enum TokenModifier { - 'declaration', - 'static', - 'async', - 'readonly', - _sentinel + // typescript-vscode-sh-plugin encodes type and modifiers in the classification: + // TSClassification = (TokenType + 1) << 8 + TokenModifier + + const TokenTypeOffset = 8; + const TokenModifierMask = (1 << TokenTypeOffset) - 1; // 0xFF + + export function getTokenTypeFromClassification(tsClassification: number): number | undefined { + if (tsClassification > TokenModifierMask) { + return (tsClassification >> TokenTypeOffset) - 1; + } + return undefined; + } + + export function getTokenModifierFromClassification(tsClassification: number) { + return tsClassification & TokenModifierMask; + } + + // Don't change TokenType and TokenModifier enums without adopting typescript-vscode-sh-plugin + export enum TokenType { + 'class', + 'enum', + 'interface', + 'namespace', + 'typeParameter', + 'type', + 'parameter', + 'variable', + 'property', + 'constant', + 'function', + 'member', + _sentinel + } + + export enum TokenModifier { + 'declaration', + 'static', + 'async', + 'readonly', + _sentinel + } } // mapping for the original ExperimentalProtocol.ClassificationType from TypeScript (only used when plugin is not available) const tokenTypeMap: number[] = []; -tokenTypeMap[ExperimentalProtocol.ClassificationType.className] = TokenType.class; -tokenTypeMap[ExperimentalProtocol.ClassificationType.enumName] = TokenType.enum; -tokenTypeMap[ExperimentalProtocol.ClassificationType.interfaceName] = TokenType.interface; -tokenTypeMap[ExperimentalProtocol.ClassificationType.moduleName] = TokenType.namespace; -tokenTypeMap[ExperimentalProtocol.ClassificationType.typeParameterName] = TokenType.typeParameter; -tokenTypeMap[ExperimentalProtocol.ClassificationType.typeAliasName] = TokenType.type; -tokenTypeMap[ExperimentalProtocol.ClassificationType.parameterName] = TokenType.parameter; +tokenTypeMap[ExperimentalProtocol.ClassificationType.className] = VSCodeShPlugin.TokenType.class; +tokenTypeMap[ExperimentalProtocol.ClassificationType.enumName] = VSCodeShPlugin.TokenType.enum; +tokenTypeMap[ExperimentalProtocol.ClassificationType.interfaceName] = VSCodeShPlugin.TokenType.interface; +tokenTypeMap[ExperimentalProtocol.ClassificationType.moduleName] = VSCodeShPlugin.TokenType.namespace; +tokenTypeMap[ExperimentalProtocol.ClassificationType.typeParameterName] = VSCodeShPlugin.TokenType.typeParameter; +tokenTypeMap[ExperimentalProtocol.ClassificationType.typeAliasName] = VSCodeShPlugin.TokenType.type; +tokenTypeMap[ExperimentalProtocol.ClassificationType.parameterName] = VSCodeShPlugin.TokenType.parameter; namespace ExperimentalProtocol { diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index 98feebef484..cc6e217941d 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -626,10 +626,10 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -typescript-vscode-sh-plugin@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.4.1.tgz#6982958419ca760c0add761dd1d5f398815bee8c" - integrity sha512-IPsz/FNuk9HsjaEOsJJxGkiKGK5E4muZtjntp/BDWYZVYOj3R8JtX6++pQ0ftMOcZwxjKxeBmnS1FSUdVMifSw== +typescript-vscode-sh-plugin@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.3.2.tgz#bde9d0eba24ca5856024811fa354a9f5a7aeebe5" + integrity sha512-XsalETsSf3y7VWxk36plqHpsbl+TUDl278HEuhPHVBcNnwuTjcIq52J/CJw84xYmxmBcTIPUgIgLLS4OE5nX2A== uri-js@^4.2.2: version "4.2.2" From d02eb0ecd23317372854e653688ff91db710e015 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 14 Jan 2020 10:15:08 +0100 Subject: [PATCH 247/843] Just apply review feedback --- .../browser/viewParts/linesDecorations/linesDecorations.ts | 2 +- src/vs/editor/browser/widget/diffEditorWidget.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts index fb2970e763a..fc6a255eb83 100644 --- a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts +++ b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts @@ -97,7 +97,7 @@ export class LinesDecorationsOverlay extends DedupOverlay { const classNames = toRender[lineIndex]; let lineOutput = ''; for (let i = 0, len = classNames.length; i < len; i++) { - lineOutput += '
Date: Tue, 14 Jan 2020 10:15:58 +0100 Subject: [PATCH 248/843] remote - allow to open files from user home with tilde syntax (#83213) --- .../contrib/search/browser/openFileHandler.ts | 11 +++++----- .../tasks/browser/abstractTaskService.ts | 6 +++--- .../tasks/browser/terminalTaskSystem.ts | 14 +++---------- .../dialogs/browser/simpleFileDialog.ts | 20 ++++++------------- .../services/path/common/remotePathService.ts | 19 ++++++++++++++++-- 5 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/openFileHandler.ts b/src/vs/workbench/contrib/search/browser/openFileHandler.ts index ffb54bbfa76..e3c5e565472 100644 --- a/src/vs/workbench/contrib/search/browser/openFileHandler.ts +++ b/src/vs/workbench/contrib/search/browser/openFileHandler.ts @@ -21,7 +21,6 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IResourceInput } from 'vs/platform/editor/common/editor'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -121,11 +120,10 @@ export class OpenFileHandler extends QuickOpenHandler { @IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @ISearchService private readonly searchService: ISearchService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IRemotePathService private readonly remotePathService: IRemotePathService, @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, @IFileService private readonly fileService: IFileService, - @ILabelService private readonly labelService: ILabelService, - @IRemotePathService private readonly remotePathService: IRemotePathService, + @ILabelService private readonly labelService: ILabelService ) { super(); @@ -187,11 +185,12 @@ export class OpenFileHandler extends QuickOpenHandler { } private async getAbsolutePathResult(query: IPreparedQuery): Promise { - const detildifiedQuery = untildify(query.original, this.environmentService.userHome); + const detildifiedQuery = untildify(query.original, (await this.remotePathService.userHome).path); if ((await this.remotePathService.path).isAbsolute(detildifiedQuery)) { const resource = toLocalResource( await this.remotePathService.fileURI(detildifiedQuery), - this.workbenchEnvironmentService.configuration.remoteAuthority); + this.workbenchEnvironmentService.configuration.remoteAuthority + ); try { const stat = await this.fileService.resolve(resource); diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 19760bb59a7..7c4b1af65ad 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -70,7 +70,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { RunAutomaticTasks } from 'vs/workbench/contrib/tasks/browser/runAutomaticTasks'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; import { format } from 'vs/base/common/jsonFormatter'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { applyEdits } from 'vs/base/common/jsonEdit'; @@ -256,7 +256,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @ITerminalInstanceService private readonly terminalInstanceService: ITerminalInstanceService, - @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @IRemotePathService private readonly remotePathService: IRemotePathService, @ITextModelService private readonly textModelResolverService: ITextModelService, @IPreferencesService private readonly preferencesService: IPreferencesService ) { @@ -1316,7 +1316,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.modelService, this.configurationResolverService, this.telemetryService, this.contextService, this.environmentService, AbstractTaskService.OutputChannelId, this.fileService, this.terminalInstanceService, - this.remoteAgentService, + this.remotePathService, (workspaceFolder: IWorkspaceFolder) => { if (!workspaceFolder) { return undefined; diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 837428886f6..9815f49fdf4 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -43,7 +43,7 @@ import { URI } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { Schemas } from 'vs/base/common/network'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; import { env as processEnv, cwd as processCwd } from 'vs/base/common/process'; interface TerminalData { @@ -176,7 +176,7 @@ export class TerminalTaskSystem implements ITaskSystem { private outputChannelId: string, private fileService: IFileService, private terminalInstanceService: ITerminalInstanceService, - private remoteAgentService: IRemoteAgentService, + private remotePathService: IRemotePathService, taskSystemInfoResolver: TaskSystemInfoResolver, ) { @@ -829,14 +829,6 @@ export class TerminalTaskSystem implements ITaskSystem { return nls.localize('TerminalTaskSystem.terminalName', 'Task - {0}', needsFolderQualification ? task.getQualifiedLabel() : task.configurationProperties.name); } - private async getUserHome(): Promise { - const env = await this.remoteAgentService.getEnvironment(); - if (env) { - return env.userHome; - } - return URI.from({ scheme: Schemas.file, path: this.environmentService.userHome }); - } - private async createShellLaunchConfig(task: CustomTask | ContributedTask, workspaceFolder: IWorkspaceFolder | undefined, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: boolean | string): Promise { let shellLaunchConfig: IShellLaunchConfig; let isShellCommand = task.command.runtime === RuntimeType.Shell; @@ -867,7 +859,7 @@ export class TerminalTaskSystem implements ITaskSystem { windowsShellArgs = true; let basename = path.basename(shellLaunchConfig.executable!).toLowerCase(); // If we don't have a cwd, then the terminal uses the home dir. - const userHome = await this.getUserHome(); + const userHome = await this.remotePathService.userHome; if (basename === 'cmd.exe' && ((options.cwd && isUNC(options.cwd)) || (!options.cwd && isUNC(userHome.fsPath)))) { return undefined; } diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 8d9fcbd9fa0..d8161e31d6e 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -35,6 +35,7 @@ import { ICommandHandler } from 'vs/platform/commands/common/commands'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { normalizeDriveLetter } from 'vs/base/common/labels'; import { SaveReason } from 'vs/workbench/common/editor'; +import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; export namespace OpenLocalFileCommand { export const ID = 'workbench.action.files.openLocalFile'; @@ -134,6 +135,7 @@ export class SimpleFileDialog { @IModeService private readonly modeService: IModeService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @IRemotePathService private readonly remotePathService: IRemotePathService, @IKeybindingService private readonly keybindingService: IKeybindingService, @IContextKeyService contextKeyService: IContextKeyService, ) { @@ -154,8 +156,8 @@ export class SimpleFileDialog { public async showOpenDialog(options: IOpenDialogOptions = {}): Promise { this.scheme = this.getScheme(options.availableFileSystems, options.defaultUri); - this.userHome = await this.getUserHome(); - const newOptions = await this.getOptions(options); + this.userHome = await this.remotePathService.userHome; + const newOptions = this.getOptions(options); if (!newOptions) { return Promise.resolve(undefined); } @@ -165,9 +167,9 @@ export class SimpleFileDialog { public async showSaveDialog(options: ISaveDialogOptions): Promise { this.scheme = this.getScheme(options.availableFileSystems, options.defaultUri); - this.userHome = await this.getUserHome(); + this.userHome = await this.remotePathService.userHome; this.requiresTrailing = true; - const newOptions = await this.getOptions(options, true); + const newOptions = this.getOptions(options, true); if (!newOptions) { return Promise.resolve(undefined); } @@ -229,16 +231,6 @@ export class SimpleFileDialog { return this.remoteAgentEnvironment; } - private async getUserHome(): Promise { - if (this.scheme !== Schemas.file) { - const env = await this.getRemoteAgentEnvironment(); - if (env) { - return env.userHome; - } - } - return URI.from({ scheme: this.scheme, path: this.environmentService.userHome }); - } - private async pickResource(isSave: boolean = false): Promise { this.allowFolderSelection = !!this.options.canSelectFolders; this.allowFileSelection = !!this.options.canSelectFiles; diff --git a/src/vs/workbench/services/path/common/remotePathService.ts b/src/vs/workbench/services/path/common/remotePathService.ts index fa6b20e9c5c..344ff8c1604 100644 --- a/src/vs/workbench/services/path/common/remotePathService.ts +++ b/src/vs/workbench/services/path/common/remotePathService.ts @@ -9,6 +9,8 @@ import { URI } from 'vs/base/common/uri'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { Schemas } from 'vs/base/common/network'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; const REMOTE_PATH_SERVICE_ID = 'remotePath'; export const IRemotePathService = createDecorator(REMOTE_PATH_SERVICE_ID); @@ -16,8 +18,10 @@ export const IRemotePathService = createDecorator(REMOTE_PAT export interface IRemotePathService { _serviceBrand: undefined; - path: Promise; + readonly path: Promise; fileURI(path: string): Promise; + + readonly userHome: Promise; } /** @@ -29,7 +33,8 @@ export class RemotePathService implements IRemotePathService { private _extHostOS: Promise; constructor( - @IRemoteAgentService readonly remoteAgentService: IRemoteAgentService + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService ) { this._extHostOS = remoteAgentService.getEnvironment().then(remoteEnvironment => { return remoteEnvironment ? remoteEnvironment.os : platform.OS; @@ -76,6 +81,16 @@ export class RemotePathService implements IRemotePathService { fragment: '' }); } + + get userHome(): Promise { + return this.remoteAgentService.getEnvironment().then(env => { + if (env) { + return env.userHome; + } + + return URI.from({ scheme: Schemas.file, path: this.environmentService.userHome }); + }); + } } registerSingleton(IRemotePathService, RemotePathService, true); From 707061ce17eab1b1a4a31f393a827e4ccaa0ac3a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jan 2020 10:17:42 +0100 Subject: [PATCH 249/843] :lipstick: --- src/vs/workbench/services/path/common/remotePathService.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/services/path/common/remotePathService.ts b/src/vs/workbench/services/path/common/remotePathService.ts index 344ff8c1604..b2d63984cd4 100644 --- a/src/vs/workbench/services/path/common/remotePathService.ts +++ b/src/vs/workbench/services/path/common/remotePathService.ts @@ -84,10 +84,13 @@ export class RemotePathService implements IRemotePathService { get userHome(): Promise { return this.remoteAgentService.getEnvironment().then(env => { + + // remote: use remote environment userHome if (env) { return env.userHome; } + // local: use the userHome from environment return URI.from({ scheme: Schemas.file, path: this.environmentService.userHome }); }); } From 76429d2f58c8d309f152f0619fe0021bf5d27e14 Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Tue, 14 Jan 2020 11:27:15 +0200 Subject: [PATCH 250/843] =?UTF-8?q?Bump=20node-native-keymap=20to=202.1.1?= =?UTF-8?q?=20for=20easier=20arm64=20cross-compil=E2=80=A6=20(#86659)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump node-native-keymap to 2.1.1 for easier arm64 cross-compilation --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 922d877c743..7981b10d33a 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "jschardet": "2.1.1", "keytar": "^4.11.0", "native-is-elevated": "0.4.1", - "native-keymap": "2.1.0", + "native-keymap": "2.1.1", "native-watchdog": "1.3.0", "node-pty": "^0.10.0-beta2", "onigasm-umd": "2.2.5", diff --git a/yarn.lock b/yarn.lock index 38a8d55738f..ebee4f42715 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6213,10 +6213,10 @@ native-is-elevated@0.4.1: resolved "https://registry.yarnpkg.com/native-is-elevated/-/native-is-elevated-0.4.1.tgz#f6391aafb13441f5b949b39ae0b466b06e7f3986" integrity sha512-2vBXCXCXYKLDjP0WzrXs/AFjDb2njPR31EbGiZ1mR2fMJg211xClK1Xm19RXve35kvAL4dBKOFGCMIyc2+pPsw== -native-keymap@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-2.1.0.tgz#f3a92e647ac021fe552587b0020f8132efb03078" - integrity sha512-a5VYhjMqxe+HK5VzJM8yIcJOKkeuMSKYfmS0p7VEKSc7hM0F5IPsq7XO8KtwAgV8PJhfQVgAgyQmK8u/MQQ0aw== +native-keymap@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-2.1.1.tgz#8b6ba2d4e81b5fe44948ebe3ff88e3777b70d4d6" + integrity sha512-8ACRLXS9xMmRQVWK8YYp0zUSW4PjXuF2VGYu1kq0WYcND9yyr3BASyhopn+VXP3nDehS3Ct5FjDtsOiLEhBvmQ== native-watchdog@1.3.0: version "1.3.0" From 5a019002121c3dc8b1c0acfd640f2072c3a2ec7b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 14 Jan 2020 10:31:05 +0100 Subject: [PATCH 251/843] [semantic tokens] Remove "-" minus styles. Fixes #86282 --- .../platform/theme/common/tokenClassificationRegistry.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index 7076d918348..d7eb816aa98 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -71,11 +71,8 @@ export namespace TokenStyle { while ((match = expression.exec(fontStyle))) { switch (match[0]) { case 'bold': bold = true; break; - case '-bold': bold = false; break; case 'italic': italic = true; break; - case '-italic': italic = false; break; case 'underline': underline = true; break; - case '-underline': underline = false; break; } } } @@ -220,10 +217,10 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { }, fontStyle: { type: 'string', - description: nls.localize('schema.token.fontStyle', 'Font style of the rule: \'italic\', \'bold\' or \'underline\', \'-italic\', \'-bold\' or \'-underline\'or a combination. The empty string unsets inherited settings.'), + description: nls.localize('schema.token.fontStyle', 'Font style of the rule: \'italic\', \'bold\' or \'underline\' or a combination. The empty string unsets inherited settings.'), pattern: fontStylePattern, - patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be \'italic\', \'bold\' or \'underline\' to set a style or \'-italic\', \'-bold\' or \'-underline\' to unset or a combination. The empty string unsets all styles.'), - defaultSnippets: [{ label: nls.localize('schema.token.fontStyle.none', 'None (clear inherited style)'), bodyText: '""' }, { body: 'italic' }, { body: 'bold' }, { body: 'underline' }, { body: '-italic' }, { body: '-bold' }, { body: '-underline' }, { body: 'italic bold' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }] + patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be \'italic\', \'bold\' or \'underline\' or a combination. The empty string unsets all styles.'), + defaultSnippets: [{ label: nls.localize('schema.token.fontStyle.none', 'None (clear inherited style)'), bodyText: '""' }, { body: 'italic' }, { body: 'bold' }, { body: 'underline' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }] } }, additionalProperties: false, From 3d6aef8b9f211d2f8596ef864b412c1070564a2c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 14 Jan 2020 10:38:38 +0100 Subject: [PATCH 252/843] #87461 Small refactorings - Remove cyclic dependency between view descriptor collection and views service - use view collection for getting and listening on views given a container - return view descriptor colleciton always for a container --- .../parts/activitybar/activitybarPart.ts | 14 +-- .../browser/parts/panel/panelPart.ts | 8 +- src/vs/workbench/browser/parts/views/views.ts | 105 +++++++++--------- src/vs/workbench/common/views.ts | 9 +- .../quickopen/browser/viewPickerHandler.ts | 2 +- 5 files changed, 61 insertions(+), 77 deletions(-) diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 9cdf544c792..610109d40a4 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -190,7 +190,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { const viewContainer = this.getViewContainer(viewletDescriptor.id); if (viewContainer?.hideIfEmpty) { const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); - if (viewDescriptors?.activeViewDescriptors.length === 0) { + if (viewDescriptors.activeViewDescriptors.length === 0) { this.hideComposite(viewletDescriptor.id); // Update the composite bar by hiding } } @@ -411,10 +411,8 @@ export class ActivitybarPart extends Part implements IActivityBarService { const viewContainer = this.getViewContainer(viewlet.id); if (viewContainer?.hideIfEmpty) { const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); - if (viewDescriptors) { - this.onDidChangeActiveViews(viewlet, viewDescriptors); - this.viewletDisposables.set(viewlet.id, viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(viewlet, viewDescriptors))); - } + this.onDidChangeActiveViews(viewlet, viewDescriptors); + this.viewletDisposables.set(viewlet.id, viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(viewlet, viewDescriptors))); } } } @@ -552,10 +550,8 @@ export class ActivitybarPart extends Part implements IActivityBarService { const views: { when: string | undefined }[] = []; if (viewContainer) { const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); - if (viewDescriptors) { - for (const { when } of viewDescriptors.allViewDescriptors) { - views.push({ when: when ? when.serialize() : undefined }); - } + for (const { when } of viewDescriptors.allViewDescriptors) { + views.push({ when: when ? when.serialize() : undefined }); } } state.push({ id: compositeItem.id, name: viewlet.name, iconUrl: viewlet.iconUrl && viewlet.iconUrl.scheme === Schemas.file ? viewlet.iconUrl : undefined, views, pinned: compositeItem.pinned, order: compositeItem.order, visible: compositeItem.visible }); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index c381e6021cd..4220c47084b 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -185,10 +185,8 @@ export class PanelPart extends CompositePart implements IPanelService { const viewContainer = this.getViewContainer(panel.id); if (viewContainer?.hideIfEmpty) { const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); - if (viewDescriptors) { - this.onDidChangeActiveViews(panel, viewDescriptors); - this.panelDisposables.set(panel.id, viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(panel, viewDescriptors))); - } + this.onDidChangeActiveViews(panel, viewDescriptors); + this.panelDisposables.set(panel.id, viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(panel, viewDescriptors))); } } } @@ -296,7 +294,7 @@ export class PanelPart extends CompositePart implements IPanelService { const viewContainer = this.getViewContainer(panelDescriptor.id); if (viewContainer?.hideIfEmpty) { const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); - if (viewDescriptors?.activeViewDescriptors.length === 0) { + if (viewDescriptors.activeViewDescriptors.length === 0) { this.hideComposite(panelDescriptor.id); // Update the composite bar by hiding } } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 3ba413e80ef..5f19242c309 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -80,8 +80,11 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl private contextKeys = new CounterSet(); private items: IViewItem[] = []; - private _onDidChange: Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._register(new Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }>()); - readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._onDidChange.event; + private _onDidChangeViews: Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._register(new Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }>()); + readonly onDidChangeViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._onDidChangeViews.event; + + private _onDidChangeActiveViews: Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._register(new Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }>()); + readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._onDidChangeActiveViews.event; get activeViewDescriptors(): IViewDescriptor[] { return this.items @@ -94,35 +97,13 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl } constructor( - container: ViewContainer, @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService ) { super(); - const onRelevantViewsRegistered = filterViewRegisterEvent(container, this.viewDescriptorService.onViewsRegistered); - this._register(onRelevantViewsRegistered(this.onViewsRegistered, this)); - - const onRelevantViewsMoved = filterViewMoveEvent(container, this.viewDescriptorService.onDidChangeContainer); - this._register(onRelevantViewsMoved(({ added, removed }) => { - if (isNonEmptyArray(added)) { - this.onViewsRegistered(added); - } - if (isNonEmptyArray(removed)) { - this.onViewsDeregistered(removed); - } - })); - - const onRelevantViewsDeregistered = filterViewRegisterEvent(container, this.viewDescriptorService.onViewsDeregistered); - this._register(onRelevantViewsDeregistered(this.onViewsDeregistered, this)); - - const onRelevantContextChange = Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys)); - this._register(onRelevantContextChange(this.onContextChanged, this)); - - - this.onViewsRegistered(this.viewDescriptorService.getViews(container)); + this._register(Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys))(this.onContextChanged, this)); } - private onViewsRegistered(viewDescriptors: IViewDescriptor[]): void { + addViews(viewDescriptors: IViewDescriptor[]): void { const added: IViewDescriptor[] = []; for (const viewDescriptor of viewDescriptors) { @@ -144,12 +125,14 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl } } + this._onDidChangeViews.fire({ added: viewDescriptors, removed: [] }); + if (added.length) { - this._onDidChange.fire({ added, removed: [] }); + this._onDidChangeActiveViews.fire({ added, removed: [] }); } } - private onViewsDeregistered(viewDescriptors: IViewDescriptor[]): void { + removeViews(viewDescriptors: IViewDescriptor[]): void { const removed: IViewDescriptor[] = []; for (const viewDescriptor of viewDescriptors) { @@ -173,8 +156,10 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl } } + this._onDidChangeViews.fire({ added: [], removed: viewDescriptors }); + if (removed.length) { - this._onDidChange.fire({ added: [], removed }); + this._onDidChangeActiveViews.fire({ added: [], removed }); } } @@ -197,7 +182,7 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl } if (added.length || removed.length) { - this._onDidChange.fire({ added, removed }); + this._onDidChangeActiveViews.fire({ added, removed }); } } @@ -257,11 +242,8 @@ export class ContributableViewsModel extends Disposable { ) { super(); const viewDescriptorCollection = viewsService.getViewDescriptors(container); - - if (viewDescriptorCollection) { - this._register(viewDescriptorCollection.onDidChangeActiveViews(() => this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors))); - this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors); - } + this._register(viewDescriptorCollection.onDidChangeActiveViews(() => this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors))); + this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors); } isVisible(id: string): boolean { @@ -649,9 +631,14 @@ export class ViewsService extends Disposable implements IViewsService { this.viewDisposable.clear(); })); - this.viewContainersRegistry.all.forEach(viewContainer => this.onViewsRegistered(this.viewDescriptorService.getViews(viewContainer), viewContainer)); - this._register(this.viewDescriptorService.onViewsRegistered(({ views, viewContainer }) => this.onViewsRegistered(views, viewContainer))); - this._register(this.viewDescriptorService.onViewsDeregistered(({ views, viewContainer }) => this.onViewsDeregistered(views, viewContainer))); + this.viewContainersRegistry.all.forEach(viewContainer => { + const viewDescriptorCollection = this.viewDescriptorService.getViewDescriptors(viewContainer); + this.onViewsRegistered(viewDescriptorCollection.allViewDescriptors, viewContainer); + this._register(viewDescriptorCollection.onDidChangeViews(({ added, removed }) => { + this.onViewsRegistered(added, viewContainer); + this.onViewsDeregistered(removed, viewContainer); + })); + }); } private onViewsRegistered(views: IViewDescriptor[], container: ViewContainer): void { @@ -704,7 +691,6 @@ export class ViewsService extends Disposable implements IViewsService { } } - private async openComposite(compositeId: string, location: ViewContainerLocation, focus?: boolean): Promise { if (location === ViewContainerLocation.Sidebar) { return this.viewletService.openViewlet(compositeId, focus); @@ -792,18 +778,14 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor return this.viewContainers.get(viewId) ?? null; } - getViewDescriptors(container: ViewContainer): IViewDescriptorCollection | null { - const registeredViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).get(container.id); - if (registeredViewContainer) { - let viewDescriptorCollectionItem = this.viewDescriptorCollections.get(registeredViewContainer); - if (!viewDescriptorCollectionItem) { - // Create and register the collection if does not exist - this.onDidRegisterViewContainer(registeredViewContainer); - viewDescriptorCollectionItem = this.viewDescriptorCollections.get(registeredViewContainer); - } - return viewDescriptorCollectionItem!.viewDescriptorCollection; + getViewDescriptors(container: ViewContainer): IViewDescriptorCollection { + let viewDescriptorCollectionItem = this.viewDescriptorCollections.get(container); + if (!viewDescriptorCollectionItem) { + // Create and register the collection if does not exist + this.onDidRegisterViewContainer(container); + viewDescriptorCollectionItem = this.viewDescriptorCollections.get(container); } - return null; + return viewDescriptorCollectionItem!.viewDescriptorCollection; } moveViews(views: IViewDescriptor[], viewContainer: ViewContainer): void { @@ -821,13 +803,26 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor } } - getViews(container: ViewContainer): IViewDescriptor[] { - return this.viewsRegistry.getViews(container); - } - private onDidRegisterViewContainer(viewContainer: ViewContainer): void { const disposables = new DisposableStore(); - const viewDescriptorCollection = disposables.add(new ViewDescriptorCollection(viewContainer, this.contextKeyService, this)); + const viewDescriptorCollection = disposables.add(new ViewDescriptorCollection(this.contextKeyService)); + viewDescriptorCollection.addViews(this.viewsRegistry.getViews(viewContainer)); + + const onRelevantViewsRegistered = filterViewRegisterEvent(viewContainer, this.onViewsRegistered); + onRelevantViewsRegistered(viewDescriptors => viewDescriptorCollection.addViews(viewDescriptors), this, disposables); + + const onRelevantViewsMoved = filterViewMoveEvent(viewContainer, this.onDidChangeContainer); + onRelevantViewsMoved(({ added, removed }) => { + if (isNonEmptyArray(added)) { + viewDescriptorCollection.addViews(added); + } + if (isNonEmptyArray(removed)) { + viewDescriptorCollection.removeViews(removed); + } + }, this, disposables); + + const onRelevantViewsDeregistered = filterViewRegisterEvent(viewContainer, this.onViewsDeregistered); + onRelevantViewsDeregistered(viewDescriptors => viewDescriptorCollection.removeViews(viewDescriptors), this, disposables); this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] }); viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed), this, disposables); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 72f90b88cd6..7e8cecb2ffa 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -202,6 +202,7 @@ export interface IViewDescriptor { } export interface IViewDescriptorCollection extends IDisposable { + readonly onDidChangeViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[] }>; readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[] }>; readonly activeViewDescriptors: IViewDescriptor[]; readonly allViewDescriptors: IViewDescriptor[]; @@ -360,17 +361,11 @@ export interface IViewDescriptorService { _serviceBrand: undefined; - readonly onViewsRegistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }>; - - readonly onViewsDeregistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }>; - readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>; moveViews(views: IViewDescriptor[], viewContainer: ViewContainer): void; - getViews(container: ViewContainer): IViewDescriptor[]; - - getViewDescriptors(container: ViewContainer): IViewDescriptorCollection | null; + getViewDescriptors(container: ViewContainer): IViewDescriptorCollection; getViewContainer(viewId: string): ViewContainer | null; } diff --git a/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts b/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts index eefa4456728..88d9c55a925 100644 --- a/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts @@ -199,7 +199,7 @@ export class ViewPickerHandler extends QuickOpenHandler { const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).get(viewlet.id); if (viewContainer?.hideIfEmpty) { const viewsCollection = this.viewDescriptorService.getViewDescriptors(viewContainer); - return !!viewsCollection && viewsCollection.activeViewDescriptors.length > 0; + return viewsCollection.activeViewDescriptors.length > 0; } return true; } From 611f25cce24ee2a4a05365a21181977680616e63 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jan 2020 10:47:29 +0100 Subject: [PATCH 253/843] :lipstick: --- .../browser/browserTextFileService.ts | 2 +- .../textfile/browser/textFileService.ts | 37 +++----- .../textfile/common/textFileEditorModel.ts | 94 ++++++++++++------- 3 files changed, 74 insertions(+), 59 deletions(-) diff --git a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts index 37bbc4bc070..6ef4e3b0b91 100644 --- a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts +++ b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts @@ -59,7 +59,7 @@ export class BrowserTextFileService extends AbstractTextFileService { } protected async getWindowCount(): Promise { - return 1; // Browser only ever is 1 window + return 1; // web: we only track 1 window, not multiple } } diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 3c0ece2ce43..8a6c4157362 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { Emitter, AsyncEmitter } from 'vs/base/common/event'; import * as platform from 'vs/base/common/platform'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, ITextFileEditorModelManager, ITextFileEditorModel, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, FileOperationWillRunEvent, FileOperationDidRunEvent, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, ITextFileEditorModel, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, FileOperationWillRunEvent, FileOperationDidRunEvent, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { IRevertOptions, IEncodingSupport } from 'vs/workbench/common/editor'; import { ILifecycleService, ShutdownReason, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -56,8 +56,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex //#endregion - private _models: TextFileEditorModelManager; - get models(): ITextFileEditorModelManager { return this._models; } + readonly models = this._register(this.instantiationService.createInstance(TextFileEditorModelManager)); abstract get encoding(): IResourceEncodings; @@ -82,13 +81,9 @@ export abstract class AbstractTextFileService extends Disposable implements ITex ) { super(); - this._models = this._register(instantiationService.createInstance(TextFileEditorModelManager)); - this.registerListeners(); } - //#region event handling - private registerListeners(): void { // Lifecycle @@ -96,6 +91,8 @@ export abstract class AbstractTextFileService extends Disposable implements ITex this.lifecycleService.onShutdown(this.dispose, this); } + //#region shutdown / backup handling + protected onBeforeShutdown(reason: ShutdownReason): boolean | Promise { // Dirty files need treatment on shutdown @@ -277,7 +274,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex //#endregion - //#region primitives (read, create, move, delete, update) + //#region text file IO primitives (read, create, move, delete, update) async read(resource: URI, options?: IReadTextFileOptions): Promise { const content = await this.fileService.readFile(resource, options); @@ -479,7 +476,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex //#endregion - //#region save/revert + //#region save async save(resource: URI, options?: ITextFileSaveOptions): Promise { return !(await this.saveAll([resource], options)).results.some(result => result.error); @@ -660,7 +657,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return models; } - return this._models.getAll(arg1); + return this.models.getAll(arg1); } private getDirtyFileModels(resources?: URI | URI[]): ITextFileEditorModel[] { @@ -699,7 +696,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // If the source is an existing text file model, we can directly // use that model to copy the contents to the target destination - const textFileModel = this._models.get(source); + const textFileModel = this.models.get(source); if (textFileModel && textFileModel.isResolved()) { success = await this.doSaveAsTextFile(textFileModel, source, target, options); } @@ -846,6 +843,10 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return untitledResource.with({ path: untitledFileName }); } + //#endregion + + //#region revert + async revert(resource: URI, options?: IRevertOptions): Promise { return !(await this.revertAll([resource], options)).results.some(result => result.error); } @@ -900,6 +901,8 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return { results: mapResourceToResult.values() }; } + //#endregion + getDirty(resources?: URI[]): URI[] { // Collect files @@ -914,21 +917,11 @@ export abstract class AbstractTextFileService extends Disposable implements ITex isDirty(resource?: URI): boolean { // Check for dirty file - if (this._models.getAll(resource).some(model => model.isDirty())) { + if (this.models.getAll(resource).some(model => model.isDirty())) { return true; } // Check for dirty untitled return this.untitledTextEditorService.getDirty().some(dirty => !resource || dirty.toString() === resource.toString()); } - - //#endregion - - dispose(): void { - - // Clear all caches - this._models.clear(); - - super.dispose(); - } } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 7abf65e410c..0c0917181d3 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -187,6 +187,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.preferredMode = mode; } + //#region Backup + async backup(target = this.resource): Promise { if (this.isResolved()) { @@ -210,6 +212,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.backupFileService.hasBackupSync(this.resource, this.versionId); } + //#endregion + + //#region Revert + async revert(options?: IRevertOptions): Promise { if (!this.isResolved()) { return false; @@ -217,7 +223,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Unset flags const wasDirty = this.dirty; - const undo = this.setDirty(false); + const undo = this.doSetDirty(false); // Force read from disk unless reverting soft const softUndo = options?.soft; @@ -244,6 +250,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return true; } + //#endregion + + //#region Load + async load(options?: ILoadOptions): Promise { this.logService.trace('[text file model] load() - enter', this.resource.toString()); @@ -349,7 +359,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Guard against the model having changed in the meantime if (currentVersionId === this.versionId) { - this.setDirty(false); // Ensure we are not tracking a stale state + this.doSetDirty(false); // Ensure we are not tracking a stale state } return this; @@ -423,7 +433,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Ensure we are not tracking a stale state else { - this.setDirty(false); + this.doSetDirty(false); } // Model Listeners @@ -434,7 +444,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.logService.trace('[text file model] load() - updated text editor model', this.resource.toString()); // Ensure we are not tracking a stale state - this.setDirty(false); + this.doSetDirty(false); // Update model value in a block that ignores model content change events this.blockModelContentChange = true; @@ -479,7 +489,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Clear flags const wasDirty = this.dirty; - this.setDirty(false); + this.doSetDirty(false); // Emit event if (wasDirty) { @@ -497,6 +507,14 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this._onDidChangeContent.fire(); } + //#endregion + + //#region Dirty + + isDirty(): this is IResolvedTextFileEditorModel { + return this.dirty; + } + makeDirty(): void { if (!this.isResolved()) { return; // only resolved models can be marked dirty @@ -509,7 +527,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Track dirty state and version id const wasDirty = this.dirty; - this.setDirty(true); + this.doSetDirty(true); // Emit as Event if we turned dirty if (!wasDirty) { @@ -517,6 +535,34 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } + private doSetDirty(dirty: boolean): () => void { + const wasDirty = this.dirty; + const wasInConflictMode = this.inConflictMode; + const wasInErrorMode = this.inErrorMode; + const oldBufferSavedVersionId = this.bufferSavedVersionId; + + if (!dirty) { + this.dirty = false; + this.inConflictMode = false; + this.inErrorMode = false; + this.updateSavedVersionId(); + } else { + this.dirty = true; + } + + // Return function to revert this call + return () => { + this.dirty = wasDirty; + this.inConflictMode = wasInConflictMode; + this.inErrorMode = wasInErrorMode; + this.bufferSavedVersionId = oldBufferSavedVersionId; + }; + } + + //#endregion + + //#region Save + async save(options: ITextFileSaveOptions = Object.create(null)): Promise { if (!this.isResolved()) { return false; @@ -666,7 +712,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Update dirty state unless model has changed meanwhile if (versionId === this.versionId) { this.logService.trace(`[text file model] doSave(${versionId}) - setting dirty to false because versionId did not change`, this.resource.toString()); - this.setDirty(false); + this.doSetDirty(false); } else { this.logService.trace(`[text file model] doSave(${versionId}) - not setting dirty to false because versionId did change meanwhile`, this.resource.toString()); } @@ -718,30 +764,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil }, error => onUnexpectedError(error) /* just log any error but do not notify the user since the file was not dirty */)); } - private setDirty(dirty: boolean): () => void { - const wasDirty = this.dirty; - const wasInConflictMode = this.inConflictMode; - const wasInErrorMode = this.inErrorMode; - const oldBufferSavedVersionId = this.bufferSavedVersionId; - - if (!dirty) { - this.dirty = false; - this.inConflictMode = false; - this.inErrorMode = false; - this.updateSavedVersionId(); - } else { - this.dirty = true; - } - - // Return function to revert this call - return () => { - this.dirty = wasDirty; - this.inConflictMode = wasInConflictMode; - this.inErrorMode = wasInErrorMode; - this.bufferSavedVersionId = oldBufferSavedVersionId; - }; - } - private updateSavedVersionId(): void { // we remember the models alternate version id to remember when the version // of the model matches with the saved version on disk. we need to keep this @@ -784,9 +806,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil TextFileEditorModel.saveErrorHandler.onSaveError(error, this); } - isDirty(): this is IResolvedTextFileEditorModel { - return this.dirty; - } + //#endregion getLastSaveAttemptTime(): number { return this.lastSaveAttemptTime; @@ -819,6 +839,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.preferredMode; } + //#region Encoding + getEncoding(): string | undefined { return this.preferredEncoding || this.contentEncoding; } @@ -883,6 +905,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return true; } + //#endregion + isResolved(): this is IResolvedTextFileEditorModel { return !!this.textEditorModel; } @@ -908,5 +932,3 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil super.dispose(); } } - - From 7c8b0844ec5f136bd6290e90bc0dd5d36ee1d04a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jan 2020 10:48:05 +0100 Subject: [PATCH 254/843] fix #87330 --- .../vscode-api-tests/src/singlefolder-tests/window.test.ts | 6 +----- extensions/vscode-api-tests/src/utils.ts | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index 1e6d2f60669..620ce762632 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -145,23 +145,19 @@ suite('window namespace tests', () => { }); }); - test.skip('active editor not always correct... #49125', async function () { + test('active editor not always correct... #49125', async function () { const randomFile1 = await createRandomFile(); const randomFile2 = await createRandomFile(); - console.log('Created random files: ' + randomFile1.toString() + ' and ' + randomFile2.toString()); - const [docA, docB] = await Promise.all([ workspace.openTextDocument(randomFile1), workspace.openTextDocument(randomFile2) ]); for (let c = 0; c < 4; c++) { let editorA = await window.showTextDocument(docA, ViewColumn.One); - console.log('Showing: ' + editorA.document.fileName + ' and active editor is: ' + window.activeTextEditor?.document.fileName); assert.equal(window.activeTextEditor, editorA); let editorB = await window.showTextDocument(docB, ViewColumn.Two); - console.log('Showing: ' + editorB.document.fileName + ' and active editor is: ' + window.activeTextEditor?.document.fileName); assert.equal(window.activeTextEditor, editorB); } }); diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index 969a7cd0051..0058c215f87 100644 --- a/extensions/vscode-api-tests/src/utils.ts +++ b/extensions/vscode-api-tests/src/utils.ts @@ -46,7 +46,6 @@ export function pathEquals(path1: string, path2: string): boolean { export function closeAllEditors(): Thenable { return vscode.commands.executeCommand('workbench.action.closeAllEditors'); - } export function disposeAll(disposables: vscode.Disposable[]) { From 926407a8b52e078e4d2347f52a43f4609459f82c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 14 Jan 2020 10:58:10 +0100 Subject: [PATCH 255/843] fix semantic token style caching --- src/vs/editor/common/services/modelServiceImpl.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 95b967ab215..b32c5fcd026 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -598,11 +598,12 @@ class SemanticColoringProviderStyling { } else { const tokenType = this._legend.tokenTypes[tokenTypeIndex]; const tokenModifiers: string[] = []; - for (let modifierIndex = 0; tokenModifierSet !== 0 && modifierIndex < this._legend.tokenModifiers.length; modifierIndex++) { - if (tokenModifierSet & 1) { + let modifierSet = tokenModifierSet; + for (let modifierIndex = 0; modifierSet > 0 && modifierIndex < this._legend.tokenModifiers.length; modifierIndex++) { + if (modifierSet & 1) { tokenModifiers.push(this._legend.tokenModifiers[modifierIndex]); } - tokenModifierSet = tokenModifierSet >> 1; + modifierSet = modifierSet >> 1; } metadata = this._themeService.getTheme().getTokenStyleMetadata(tokenType, tokenModifiers); From 160a1c69787f8c97a5edca6ea2ba34bb36710838 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 14 Jan 2020 10:50:29 +0100 Subject: [PATCH 256/843] suggest - dont force widget creation --- .../contrib/suggest/suggestController.ts | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index cdf2e4f41ef..4ea61b2929c 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -165,7 +165,20 @@ export class SuggestController implements IEditorContribution { })); this._toDispose.add(toDisposable(() => makesTextEdit.reset())); + this._toDispose.add(widget.onDetailsKeyDown(e => { + // cmd + c on macOS, ctrl + c on Win / Linux + if ( + e.toKeybinding().equals(new SimpleKeybinding(true, false, false, false, KeyCode.KEY_C)) || + (platform.isMacintosh && e.toKeybinding().equals(new SimpleKeybinding(false, false, false, true, KeyCode.KEY_C))) + ) { + e.stopPropagation(); + return; + } + if (!e.toKeybinding().isModifierKey()) { + this.editor.focus(); + } + })); return widget; })); @@ -198,21 +211,6 @@ export class SuggestController implements IEditorContribution { } })); - this._toDispose.add(this.widget.getValue().onDetailsKeyDown(e => { - // cmd + c on macOS, ctrl + c on Win / Linux - if ( - e.toKeybinding().equals(new SimpleKeybinding(true, false, false, false, KeyCode.KEY_C)) || - (platform.isMacintosh && e.toKeybinding().equals(new SimpleKeybinding(false, false, false, true, KeyCode.KEY_C))) - ) { - e.stopPropagation(); - return; - } - - if (!e.toKeybinding().isModifierKey()) { - this.editor.focus(); - } - })); - // Manage the acceptSuggestionsOnEnter context key let acceptSuggestionsOnEnter = SuggestContext.AcceptSuggestionsOnEnter.bindTo(_contextKeyService); let updateFromConfig = () => { From 31ef68ff7c293bfbd781cce00aac730e0a3d00eb Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 14 Jan 2020 11:08:58 +0100 Subject: [PATCH 257/843] Adopt latest istanbul libraries --- package.json | 10 +- test/coverage.js | 51 +++--- test/electron/renderer.js | 2 +- yarn.lock | 322 ++++++++++++++++++++++---------------- 4 files changed, 219 insertions(+), 166 deletions(-) diff --git a/package.json b/package.json index 7981b10d33a..71baf2eb073 100644 --- a/package.json +++ b/package.json @@ -121,11 +121,11 @@ "husky": "^0.13.1", "innosetup": "5.6.1", "is": "^3.1.0", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.0", "jsdom-no-contextify": "^3.1.0", "lazy.js": "^0.4.2", "merge-options": "^1.0.1", diff --git a/test/coverage.js b/test/coverage.js index bf7e7aa3f95..1964dfc4bd0 100644 --- a/test/coverage.js +++ b/test/coverage.js @@ -22,7 +22,7 @@ exports.initialize = function (loaderConfig) { return contents; } // Try to find a .map file - let map = null; + let map = undefined; try { map = JSON.parse(fs.readFileSync(`${source}.map`).toString()); } catch (err) { @@ -35,32 +35,33 @@ exports.initialize = function (loaderConfig) { exports.createReport = function (isSingle) { const mapStore = iLibSourceMaps.createSourceMapStore(); const coverageMap = iLibCoverage.createCoverageMap(global.__coverage__); - const transformed = mapStore.transformCoverage(coverageMap); + return mapStore.transformCoverage(coverageMap).then((transformed) => { + // Paths come out all broken + let newData = Object.create(null); + Object.keys(transformed.data).forEach((file) => { + const entry = transformed.data[file]; + const fixedPath = fixPath(entry.path); + entry.data.path = fixedPath; + newData[fixedPath] = entry; + }); + transformed.data = newData; - // Paths come out all broken - let newData = Object.create(null); - Object.keys(transformed.map.data).forEach((file) => { - const entry = transformed.map.data[file]; - const fixedPath = fixPath(entry.path); - entry.data.path = fixedPath; - newData[fixedPath] = entry; + const context = iLibReport.createContext({ + dir: path.join(__dirname, `../.build/coverage${isSingle ? '-single' : ''}`), + coverageMap: transformed + }); + const tree = context.getTree('flat'); + + let reports = []; + if (isSingle) { + reports.push(iReports.create('lcovonly')); + } else { + reports.push(iReports.create('json')); + reports.push(iReports.create('lcov')); + reports.push(iReports.create('html')); + } + reports.forEach(report => tree.visit(report, context)); }); - transformed.map.data = newData; - - const tree = iLibReport.summarizers.flat(transformed.map); - const context = iLibReport.createContext({ - dir: path.join(__dirname, `../.build/coverage${isSingle ? '-single' : ''}`) - }); - - let reports = []; - if (isSingle) { - reports.push(iReports.create('lcovonly')); - } else { - reports.push(iReports.create('json')); - reports.push(iReports.create('lcov')); - reports.push(iReports.create('html')); - } - reports.forEach(report => tree.visit(report, context)); }; function toUpperDriveLetter(str) { diff --git a/test/electron/renderer.js b/test/electron/renderer.js index 36668cd53b3..152abff3746 100644 --- a/test/electron/renderer.js +++ b/test/electron/renderer.js @@ -50,7 +50,7 @@ function initLoader(opts) { function createCoverageReport(opts) { if (opts.coverage) { - coverage.createReport(opts.run || opts.runGlob); + return coverage.createReport(opts.run || opts.runGlob); } return Promise.resolve(undefined); } diff --git a/yarn.lock b/yarn.lock index ebee4f42715..334e3f30543 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,39 +14,75 @@ dependencies: "@babel/highlight" "^7.0.0" -"@babel/generator@^7.4.0", "@babel/generator@^7.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.0.tgz#f20e4b7a91750ee8b63656073d843d2a736dca4a" - integrity sha512-1TTVrt7J9rcG5PMjvO7VEG3FrEoEJNHxumRq66GemPmzboLWtIjjcJgk8rokuAS7IiRSpgVSu5Vb9lc99iJkOA== +"@babel/code-frame@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" + integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== dependencies: - "@babel/types" "^7.5.0" - jsesc "^2.5.1" - lodash "^4.17.11" + "@babel/highlight" "^7.8.3" + +"@babel/core@^7.7.5": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.3.tgz#30b0ebb4dd1585de6923a0b4d179e0b9f5d82941" + integrity sha512-4XFkf8AwyrEG7Ziu3L2L0Cv+WyY47Tcsp70JFmpftbAA1K7YL/sgE9jh9HyNj08Y/U50ItUchpN0w6HxAoX1rA== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.8.3" + "@babel/helpers" "^7.8.3" + "@babel/parser" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.0" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" source-map "^0.5.0" - trim-right "^1.0.1" -"@babel/helper-function-name@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" - integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw== +"@babel/generator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.3.tgz#0e22c005b0a94c1c74eafe19ef78ce53a4d45c03" + integrity sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug== dependencies: - "@babel/helper-get-function-arity" "^7.0.0" - "@babel/template" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/types" "^7.8.3" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" -"@babel/helper-get-function-arity@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3" - integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ== +"@babel/helper-function-name@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" + integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA== dependencies: - "@babel/types" "^7.0.0" + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" -"@babel/helper-split-export-declaration@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" - integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q== +"@babel/helper-get-function-arity@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" + integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== dependencies: - "@babel/types" "^7.4.4" + "@babel/types" "^7.8.3" + +"@babel/helper-split-export-declaration@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" + integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helpers@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.3.tgz#382fbb0382ce7c4ce905945ab9641d688336ce85" + integrity sha512-LmU3q9Pah/XyZU89QvBgGt+BCsTPoQa+73RxAQh8fb8qkDyIfeQnmgs+hvzhTCKTzqOyk7JTkS3MS1S8Mq5yrQ== + dependencies: + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" "@babel/highlight@^7.0.0": version "7.0.0" @@ -57,42 +93,51 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.0.tgz#3e0713dff89ad6ae37faec3b29dcfc5c979770b7" - integrity sha512-I5nW8AhGpOXGCCNYGc+p7ExQIBxRFnS2fd/d862bNOKvmoEPjYPcfIjsfdy0ujagYOIYPczKgD9l3FsgTkAzKA== - -"@babel/template@^7.1.0", "@babel/template@^7.4.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237" - integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw== +"@babel/highlight@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" + integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.4.4" - "@babel/types" "^7.4.4" + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" -"@babel/traverse@^7.4.3": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.0.tgz#4216d6586854ef5c3c4592dab56ec7eb78485485" - integrity sha512-SnA9aLbyOCcnnbQEGwdfBggnc142h/rbqqsXcaATj2hZcegCl903pUD/lfpsNBlBSuWow/YDfRyJuWi2EPR5cg== +"@babel/parser@^7.7.5", "@babel/parser@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.3.tgz#790874091d2001c9be6ec426c2eed47bc7679081" + integrity sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ== + +"@babel/template@^7.7.4", "@babel/template@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8" + integrity sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ== dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.5.0" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.4.4" - "@babel/parser" "^7.5.0" - "@babel/types" "^7.5.0" + "@babel/code-frame" "^7.8.3" + "@babel/parser" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/traverse@^7.7.4", "@babel/traverse@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.3.tgz#a826215b011c9b4f73f3a893afbc05151358bf9a" + integrity sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.8.3" + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/parser" "^7.8.3" + "@babel/types" "^7.8.3" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.11" + lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.0.tgz#e47d43840c2e7f9105bc4d3a2c371b4d0c7832ab" - integrity sha512-UFpDVqRABKsW01bvw7/wSUe56uy6RXM5+VJibVVAybDGxEW25jdwiFJEf7ASvSaC7sN7rbE/l3cLp2izav+CtQ== +"@babel/types@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" + integrity sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg== dependencies: esutils "^2.0.2" - lodash "^4.17.11" + lodash "^4.17.13" to-fast-properties "^2.0.0" "@electron/get@^1.0.1": @@ -110,6 +155,11 @@ global-agent "^2.0.2" global-tunnel-ng "^2.7.1" +"@istanbuljs/schema@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" + integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -1934,11 +1984,6 @@ commander@~2.13.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== -commander@~2.20.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" - integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== - commandpost@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/commandpost/-/commandpost-1.2.1.tgz#2e9c4c7508b9dc704afefaa91cab92ee6054cc68" @@ -2036,6 +2081,13 @@ convert-source-map@^1.5.0: dependencies: safe-buffer "~5.1.1" +convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -3724,6 +3776,11 @@ generate-object-property@^1.1.0: dependencies: is-property "^1.0.0" +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" @@ -4314,17 +4371,6 @@ gulplog@^1.0.0: dependencies: glogg "^1.0.0" -handlebars@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67" - integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw== - dependencies: - neo-async "^2.6.0" - optimist "^0.6.1" - source-map "^0.6.1" - optionalDependencies: - uglify-js "^3.1.4" - har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -4378,6 +4424,11 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" @@ -4505,6 +4556,11 @@ html-comment-regex@^1.1.0: resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" integrity sha1-ZouTd26q5V696POtRkswekljYl4= +html-escaper@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491" + integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig== + "htmlparser2@>= 3.7.3 < 4.0.0", htmlparser2@^3.9.1: version "3.9.2" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" @@ -5157,50 +5213,49 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" - integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== +istanbul-lib-coverage@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== -istanbul-lib-instrument@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" - integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== +istanbul-lib-instrument@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.0.tgz#53321a7970f076262fd3292c8f9b2e4ac544aae1" + integrity sha512-Nm4wVHdo7ZXSG30KjZ2Wl5SU/Bw7bDx1PdaiIFzEStdjs0H12mOTncn1GVYuqQSaZxpg87VGBRsVRPGD2cD1AQ== dependencies: - "@babel/generator" "^7.4.0" - "@babel/parser" "^7.4.3" - "@babel/template" "^7.4.0" - "@babel/traverse" "^7.4.3" - "@babel/types" "^7.4.0" - istanbul-lib-coverage "^2.0.5" - semver "^6.0.0" + "@babel/core" "^7.7.5" + "@babel/parser" "^7.7.5" + "@babel/template" "^7.7.4" + "@babel/traverse" "^7.7.4" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" -istanbul-lib-report@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" - integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== dependencies: - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - supports-color "^6.1.0" + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" -istanbul-lib-source-maps@^3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" - integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== dependencies: debug "^4.1.1" - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - rimraf "^2.6.3" + istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^2.2.6: - version "2.2.6" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.6.tgz#7b4f2660d82b29303a8fe6091f8ca4bf058da1af" - integrity sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA== +istanbul-reports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.0.tgz#d4d16d035db99581b6194e119bbf36c963c5eb70" + integrity sha512-2osTcC8zcOSUkImzN2EWQta3Vdi4WjjKw99P2yWx5mLnigAM0Rd5uYFn1cf2i/Ois45GkNjaoTqc5CxgMSX80A== dependencies: - handlebars "^4.1.2" + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" istextorbinary@1.0.2: version "1.0.2" @@ -5367,6 +5422,13 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +json5@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" + integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== + dependencies: + minimist "^1.2.0" + jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" @@ -5674,7 +5736,7 @@ lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== -lodash@^4.17.14, lodash@^4.17.15: +lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -5742,13 +5804,12 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" -make-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== +make-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.0.tgz#1b5f39f6b9270ed33f9f054c5c0f84304989f801" + integrity sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw== dependencies: - pify "^4.0.1" - semver "^5.6.0" + semver "^6.0.0" make-iterator@^1.0.0: version "1.0.1" @@ -6247,11 +6308,6 @@ neo-async@^2.5.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" integrity sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA== -neo-async@^2.6.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" - integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== - nice-try@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" @@ -7017,11 +7073,6 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -7999,6 +8050,13 @@ resolve@^1.1.6, resolve@^1.1.7: dependencies: path-parse "^1.0.5" +resolve@^1.3.2: + version "1.14.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" + integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== + dependencies: + path-parse "^1.0.6" + resolve@^1.4.0: version "1.10.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" @@ -8034,7 +8092,7 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -rimraf@2, rimraf@2.6.3, rimraf@^2.6.3: +rimraf@2, rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== @@ -8828,7 +8886,7 @@ supports-color@1.2.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" integrity sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4= -supports-color@6.1.0, supports-color@^6.1.0: +supports-color@6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== @@ -8861,6 +8919,13 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + sver-compat@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" @@ -9154,11 +9219,6 @@ tough-cookie@~2.4.3: resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk= -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" - integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= - truncate-utf8-bytes@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b" @@ -9285,14 +9345,6 @@ uglify-es@^3.3.4: commander "~2.13.0" source-map "~0.6.1" -uglify-js@^3.1.4: - version "3.6.0" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5" - integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg== - dependencies: - commander "~2.20.0" - source-map "~0.6.1" - uglifyjs-webpack-plugin@^1.2.4: version "1.2.7" resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.7.tgz#57638dd99c853a1ebfe9d97b42160a8a507f9d00" From c96aece131c24c3b3facfcdf042281617f8e1373 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 14 Jan 2020 11:30:31 +0100 Subject: [PATCH 258/843] tree: respect isChecked and getRole #88553 --- src/vs/base/browser/ui/tree/abstractTree.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 732edc52118..2b2ed1d052d 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -190,7 +190,13 @@ function asListOptions(modelProvider: () => ITreeModel { + return options.ariaProvider!.isChecked!(node.element); + } : undefined, + getRole: options.ariaProvider && options.ariaProvider.getRole ? (node) => { + return options.ariaProvider!.getRole!(node.element); + } : undefined } }; } From da5d2781b03d337f581991eca8ca43e0a988877f Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 14 Jan 2020 11:30:51 +0100 Subject: [PATCH 259/843] replace console with logService. For #84283 --- src/vs/workbench/api/node/extHostCLIServer.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts index 1ccb71e52a9..ad28cc495c0 100644 --- a/src/vs/workbench/api/node/extHostCLIServer.ts +++ b/src/vs/workbench/api/node/extHostCLIServer.ts @@ -11,6 +11,7 @@ import { IWindowOpenable } from 'vs/platform/windows/common/windows'; import { URI } from 'vs/base/common/uri'; import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; import { INativeOpenWindowOptions } from 'vs/platform/windows/node/window'; +import { ILogService } from 'vs/platform/log/common/log'; export interface OpenCommandPipeArgs { type: 'open'; @@ -39,10 +40,10 @@ export class CLIServer { private _server: http.Server; private _ipcHandlePath: string | undefined; - constructor(@IExtHostCommands private _commands: IExtHostCommands) { + constructor(@IExtHostCommands private _commands: IExtHostCommands, @ILogService private logService: ILogService) { this._server = http.createServer((req, res) => this.onRequest(req, res)); this.setup().catch(err => { - console.error(err); + logService.error(err); return ''; }); } @@ -56,9 +57,9 @@ export class CLIServer { try { this._server.listen(this.ipcHandlePath); - this._server.on('error', err => console.error(err)); + this._server.on('error', err => this.logService.error(err)); } catch (err) { - console.error('Could not start open from terminal server.'); + this.logService.error('Could not start open from terminal server.'); } return this._ipcHandlePath; @@ -79,13 +80,13 @@ export class CLIServer { break; case 'command': this.runCommand(data, res) - .catch(console.error); + .catch(this.logService.error); break; default: res.writeHead(404); res.write(`Unknown message type: ${data.type}`, err => { if (err) { - console.error(err); + this.logService.error(err); } }); res.end(); @@ -139,7 +140,7 @@ export class CLIServer { res.writeHead(500); res.write(String(err), err => { if (err) { - console.error(err); + this.logService.error(err); } }); res.end(); @@ -153,7 +154,7 @@ export class CLIServer { res.writeHead(200); res.write(JSON.stringify(result), err => { if (err) { - console.error(err); + this.logService.error(err); } }); res.end(); @@ -161,7 +162,7 @@ export class CLIServer { res.writeHead(500); res.write(String(err), err => { if (err) { - console.error(err); + this.logService.error(err); } }); res.end(); From f43c3601e5feb57cc4dfe9c033881f1886d1d7ff Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 14 Jan 2020 11:31:29 +0100 Subject: [PATCH 260/843] tokenStyleResolving: fix test --- .../themes/test/electron-browser/tokenStyleResolving.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts index 0e84f4e22c4..6e2a7b23ac7 100644 --- a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts +++ b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts @@ -306,7 +306,7 @@ suite('Themes - TokenStyleResolving', () => { '*.static': { fontStyle: 'bold' }, '*.declaration': { fontStyle: 'italic' }, '*.async.static': { fontStyle: 'italic underline' }, - '*.async': { foreground: '#000fff', fontStyle: '-italic underline' } + '*.async': { foreground: '#000fff', fontStyle: 'underline' } }); assertTokenStyles(themeData, { @@ -316,7 +316,7 @@ suite('Themes - TokenStyleResolving', () => { 'class': ts('#0000ff', { italic: true }), 'class.static.declaration': ts('#0000ff', { bold: true, italic: true }), 'class.declaration': ts('#0000ff', { italic: true }), - 'class.declaration.async': ts('#000fff', { underline: true, italic: false }), + 'class.declaration.async': ts('#000fff', { underline: true, italic: true }), 'class.declaration.async.static': ts('#000fff', { italic: true, underline: true, bold: true }), }); From 1420967188b1a2ef98326cbb30b0fa5fc219b1c4 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 14 Jan 2020 12:18:20 +0100 Subject: [PATCH 261/843] fixes #86848 --- build/win32/code.iss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/win32/code.iss b/build/win32/code.iss index 5c995f26636..15293a0c5cf 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -970,10 +970,10 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\*\shell\{#RegValueNam Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "{cm:OpenWithCodeContextMenu,{#ShellNameShort}}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\shell\{#RegValueName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\{#ExeBasename}.exe"; Tasks: addcontextmenufolders Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\shell\{#RegValueName}\command"; ValueType: expandsz; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%V"""; Tasks: addcontextmenufolders -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\background\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open w&ith {#ShellNameShort}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\background\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "{cm:OpenWithCodeContextMenu,{#ShellNameShort}}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\background\shell\{#RegValueName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\{#ExeBasename}.exe"; Tasks: addcontextmenufolders Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\background\shell\{#RegValueName}\command"; ValueType: expandsz; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%V"""; Tasks: addcontextmenufolders -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open w&ith {#ShellNameShort}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "{cm:OpenWithCodeContextMenu,{#ShellNameShort}}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValueName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\{#ExeBasename}.exe"; Tasks: addcontextmenufolders Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValueName}\command"; ValueType: expandsz; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%V"""; Tasks: addcontextmenufolders From 1f5a5470c5679c85960fd495b40a08f9206d0f02 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jan 2020 15:30:17 +0100 Subject: [PATCH 262/843] implement backup on shutdown via working copies (#84672) --- build/lib/i18n.resources.json | 4 + .../backup/browser/backup.web.contribution.ts | 12 + .../backup/browser/backupOnShutdown.ts | 55 ++++ .../electron-browser/backup.contribution.ts | 12 + .../electron-browser/backupOnShutdown.ts | 207 +++++++++++++ .../electron-browser/backupOnShutdown.test.ts | 283 ++++++++++++++++++ .../electron-browser/backupRestorer.test.ts | 1 - .../electron-browser/backupTracker.test.ts | 1 - .../customEditor/common/customEditorModel.ts | 4 + .../files/test/browser/editorAutoSave.test.ts | 1 - .../test/browser/fileEditorTracker.test.ts | 2 - .../browser/browserTextFileService.ts | 47 +-- .../textfile/browser/textFileService.ts | 206 +------------ .../electron-browser/nativeTextFileService.ts | 13 +- .../textfile/test/textFileEditorModel.test.ts | 2 +- .../textfile/test/textFileService.io.test.ts | 1 - .../textfile/test/textFileService.test.ts | 230 +------------- .../test/textModelResolverService.test.ts | 1 - .../workingCopy/common/workingCopyService.ts | 6 +- .../test/common/workingCopyService.test.ts | 10 +- .../api/mainThreadSaveParticipant.test.ts | 2 +- .../workbench/test/workbenchTestServices.ts | 19 +- src/vs/workbench/workbench.desktop.main.ts | 3 + src/vs/workbench/workbench.web.main.ts | 3 + 24 files changed, 627 insertions(+), 498 deletions(-) create mode 100644 src/vs/workbench/contrib/backup/browser/backup.web.contribution.ts create mode 100644 src/vs/workbench/contrib/backup/browser/backupOnShutdown.ts create mode 100644 src/vs/workbench/contrib/backup/electron-browser/backup.contribution.ts create mode 100644 src/vs/workbench/contrib/backup/electron-browser/backupOnShutdown.ts create mode 100644 src/vs/workbench/contrib/backup/test/electron-browser/backupOnShutdown.test.ts diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 6b5c3a65891..d10b90b6d92 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -30,6 +30,10 @@ "name": "vs/workbench/api/common", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/backup", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/bulkEdit", "project": "vscode-workbench" diff --git a/src/vs/workbench/contrib/backup/browser/backup.web.contribution.ts b/src/vs/workbench/contrib/backup/browser/backup.web.contribution.ts new file mode 100644 index 00000000000..e9d79317dd8 --- /dev/null +++ b/src/vs/workbench/contrib/backup/browser/backup.web.contribution.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { BackupOnShutdown } from 'vs/workbench/contrib/backup/browser/backupOnShutdown'; + +// Register Backup On Shutdown +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BackupOnShutdown, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/backup/browser/backupOnShutdown.ts b/src/vs/workbench/contrib/backup/browser/backupOnShutdown.ts new file mode 100644 index 00000000000..8c17c752552 --- /dev/null +++ b/src/vs/workbench/contrib/backup/browser/backupOnShutdown.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; + +export class BackupOnShutdown extends Disposable implements IWorkbenchContribution { + + constructor( + @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, + @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + ) { + super(); + + this.registerListeners(); + } + + private registerListeners() { + + // Lifecycle + this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown())); + } + + private onBeforeShutdown(): boolean { + + // Web: we cannot perform long running in the shutdown phase + // As such we need to check sync if there are any dirty working + // copies that have not been backed up yet and then prevent the + // shutdown if that is the case. + + const dirtyWorkingCopies = this.workingCopyService.workingCopies.filter(workingCopy => workingCopy.isDirty()); + if (!dirtyWorkingCopies.length) { + return false; // no dirty: no veto + } + + if (!this.filesConfigurationService.isHotExitEnabled) { + return true; // dirty without backup: veto + } + + for (const dirtyWorkingCopy of dirtyWorkingCopies) { + if (!dirtyWorkingCopy.hasBackup()) { + console.warn('Unload prevented: pending backups'); + return true; // dirty without backup: veto + } + } + + return false; // dirty with backups: no veto + } +} diff --git a/src/vs/workbench/contrib/backup/electron-browser/backup.contribution.ts b/src/vs/workbench/contrib/backup/electron-browser/backup.contribution.ts new file mode 100644 index 00000000000..26ac4d2a0cb --- /dev/null +++ b/src/vs/workbench/contrib/backup/electron-browser/backup.contribution.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { BackupOnShutdown } from 'vs/workbench/contrib/backup/electron-browser/backupOnShutdown'; + +// Register Backup On Shutdown +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BackupOnShutdown, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/backup/electron-browser/backupOnShutdown.ts b/src/vs/workbench/contrib/backup/electron-browser/backupOnShutdown.ts new file mode 100644 index 00000000000..8e1053caa65 --- /dev/null +++ b/src/vs/workbench/contrib/backup/electron-browser/backupOnShutdown.ts @@ -0,0 +1,207 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { ILifecycleService, LifecyclePhase, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { ConfirmResult, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { isMacintosh } from 'vs/base/common/platform'; +import { HotExitConfiguration } from 'vs/platform/files/common/files'; +import { IElectronService } from 'vs/platform/electron/node/electron'; +import type { ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor'; + +export class BackupOnShutdown extends Disposable implements IWorkbenchContribution { + + constructor( + @IBackupFileService private readonly backupFileService: IBackupFileService, + @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, + @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IFileDialogService private readonly fileDialogService: IFileDialogService, + @INotificationService private readonly notificationService: INotificationService, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IElectronService private readonly electronService: IElectronService + ) { + super(); + + this.registerListeners(); + } + + private registerListeners() { + + // Lifecycle + this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(event.reason))); + } + + private onBeforeShutdown(reason: ShutdownReason): boolean | Promise { + + // Dirty working copies need treatment on shutdown + const dirtyWorkingCopies = this.workingCopyService.workingCopies.filter(workingCopy => workingCopy.isDirty()); + if (dirtyWorkingCopies.length) { + + // If auto save is enabled, save all working copies and then check again for dirty copies + // We DO NOT run any save participant if we are in the shutdown phase for performance reasons + if (this.filesConfigurationService.getAutoSaveMode() !== AutoSaveMode.OFF) { + return this.doSaveAll(dirtyWorkingCopies, false /* not untitled */, { skipSaveParticipants: true }).then(() => { + + // If we still have dirty working copies, we either have untitled ones or working copies that cannot be saved + const remainingDirtyWorkingCopies = this.workingCopyService.workingCopies.filter(workingCopy => workingCopy.isDirty()); + if (remainingDirtyWorkingCopies.length) { + return this.handleDirtyBeforeShutdown(remainingDirtyWorkingCopies, reason); + } + + return this.noVeto({ cleanUpBackups: false }); // no veto and no backup cleanup (since there are no dirty working copies) + }); + } + + // Auto save is not enabled + return this.handleDirtyBeforeShutdown(dirtyWorkingCopies, reason); + } + + // No dirty working copies: no veto + return this.noVeto({ cleanUpBackups: true }); + } + + private handleDirtyBeforeShutdown(workingCopies: IWorkingCopy[], reason: ShutdownReason): boolean | Promise { + + // If hot exit is enabled, backup dirty working copies and allow to exit without confirmation + if (this.filesConfigurationService.isHotExitEnabled) { + return this.backupBeforeShutdown(workingCopies, reason).then(didBackup => { + if (didBackup) { + return this.noVeto({ cleanUpBackups: false }); // no veto and no backup cleanup (since backup was successful) + } + + // since a backup did not happen, we have to confirm for the dirty working copies now + return this.confirmBeforeShutdown(); + }, error => { + this.notificationService.error(localize('backupOnShutdown.failSave', "Working copies that are dirty could not be written to the backup location (Error: {0}). Try saving your editors first and then exit.", error.message)); + + return true; // veto, the backups failed + }); + } + + // Otherwise just confirm from the user what to do with the dirty working copies + return this.confirmBeforeShutdown(); + } + + private async backupBeforeShutdown(workingCopies: IWorkingCopy[], reason: ShutdownReason): Promise { + + // When quit is requested skip the confirm callback and attempt to backup all workspaces. + // When quit is not requested the confirm callback should be shown when the window being + // closed is the only VS Code window open, except for on Mac where hot exit is only + // ever activated when quit is requested. + + let doBackup: boolean | undefined; + switch (reason) { + case ShutdownReason.CLOSE: + if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.filesConfigurationService.hotExitConfiguration === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) { + doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured + } else if (await this.electronService.getWindowCount() > 1 || isMacintosh) { + doBackup = false; // do not backup if a window is closed that does not cause quitting of the application + } else { + doBackup = true; // backup if last window is closed on win/linux where the application quits right after + } + break; + + case ShutdownReason.QUIT: + doBackup = true; // backup because next start we restore all backups + break; + + case ShutdownReason.RELOAD: + doBackup = true; // backup because after window reload, backups restore + break; + + case ShutdownReason.LOAD: + if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.filesConfigurationService.hotExitConfiguration === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) { + doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured + } else { + doBackup = false; // do not backup because we are switching contexts + } + break; + } + + if (!doBackup) { + return false; + } + + // Backup all working copies + await Promise.all(workingCopies.map(workingCopy => workingCopy.backup())); + + return true; + } + + private async confirmBeforeShutdown(): Promise { + + // Show confirm dialog for all dirty working copies + const dirtyWorkingCopies = this.workingCopyService.workingCopies.filter(workingCopy => workingCopy.isDirty()); + const confirm = await this.fileDialogService.showSaveConfirm(dirtyWorkingCopies.map(w => w.resource)); + + // Save + if (confirm === ConfirmResult.SAVE) { + await this.doSaveAll(dirtyWorkingCopies, true /* includeUntitled */, { skipSaveParticipants: true }); + + if (this.workingCopyService.hasDirty) { + return true; // veto if any save failed + } + + return this.noVeto({ cleanUpBackups: true }); + } + + // Don't Save + else if (confirm === ConfirmResult.DONT_SAVE) { + + // Make sure to revert working copies so that they do not restore + // see https://github.com/Microsoft/vscode/issues/29572 + await this.doRevertAll(dirtyWorkingCopies, { soft: true } /* soft revert is good enough on shutdown */); + + return this.noVeto({ cleanUpBackups: true }); + } + + // Cancel + else if (confirm === ConfirmResult.CANCEL) { + return true; // veto + } + + return false; + } + + private doSaveAll(workingCopies: IWorkingCopy[], includeUntitled: boolean, options: ISaveOptions): Promise { + return Promise.all(workingCopies.map(async workingCopy => { + if (workingCopy.isDirty() && (includeUntitled || !(workingCopy.capabilities & WorkingCopyCapabilities.Untitled))) { + return workingCopy.save(options); + } + + return false; + })); + } + + private doRevertAll(workingCopies: IWorkingCopy[], options: IRevertOptions): Promise { + return Promise.all(workingCopies.map(workingCopy => workingCopy.revert(options))); + } + + private noVeto(options: { cleanUpBackups: boolean }): boolean | Promise { + if (!options.cleanUpBackups) { + return false; + } + + if (this.lifecycleService.phase < LifecyclePhase.Restored) { + return false; // if editors have not restored, we are not up to speed with backups and thus should not clean them + } + + if (this.environmentService.isExtensionDevelopment) { + return false; // extension development does not track any backups + } + + return this.backupFileService.discardAllWorkspaceBackups().then(() => false, () => false); + } +} diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupOnShutdown.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupOnShutdown.test.ts new file mode 100644 index 00000000000..db140201650 --- /dev/null +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupOnShutdown.test.ts @@ -0,0 +1,283 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as platform from 'vs/base/common/platform'; +import { ILifecycleService, BeforeShutdownEvent, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; +import { workbenchInstantiationService, TestLifecycleService, TestTextFileService, TestContextService, TestFileService, TestElectronService, TestFilesConfigurationService, TestFileDialogService, TestBackupFileService } from 'vs/workbench/test/workbenchTestServices'; +import { toResource } from 'vs/base/test/common/utils'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; +import { HotExitConfiguration, IFileService } from 'vs/platform/files/common/files'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs'; +import { BackupOnShutdown } from 'vs/workbench/contrib/backup/electron-browser/backupOnShutdown'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; + +class ServiceAccessor { + constructor( + @ILifecycleService public lifecycleService: TestLifecycleService, + @ITextFileService public textFileService: TestTextFileService, + @IFilesConfigurationService public filesConfigurationService: TestFilesConfigurationService, + @IUntitledTextEditorService public untitledTextEditorService: IUntitledTextEditorService, + @IWorkspaceContextService public contextService: TestContextService, + @IModelService public modelService: ModelServiceImpl, + @IFileService public fileService: TestFileService, + @IElectronService public electronService: TestElectronService, + @IFileDialogService public fileDialogService: TestFileDialogService, + @IBackupFileService public backupFileService: TestBackupFileService + ) { + } +} + +class BeforeShutdownEventImpl implements BeforeShutdownEvent { + + value: boolean | Promise | undefined; + reason = ShutdownReason.CLOSE; + + veto(value: boolean | Promise): void { + this.value = value; + } +} + +suite('BackupOnShutdown', () => { + + let instantiationService: IInstantiationService; + let model: TextFileEditorModel; + let accessor: ServiceAccessor; + let backupOnShutdown: BackupOnShutdown; + + setup(() => { + instantiationService = workbenchInstantiationService(); + accessor = instantiationService.createInstance(ServiceAccessor); + backupOnShutdown = instantiationService.createInstance(BackupOnShutdown); + }); + + teardown(() => { + if (model) { + model.dispose(); + } + (accessor.textFileService.models).dispose(); + accessor.untitledTextEditorService.revertAll(); + backupOnShutdown.dispose(); + }); + + test('confirm onWillShutdown - no veto', async function () { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + (accessor.textFileService.models).add(model.resource, model); + + const event = new BeforeShutdownEventImpl(); + accessor.lifecycleService.fireWillShutdown(event); + + const veto = event.value; + if (typeof veto === 'boolean') { + assert.ok(!veto); + } else { + assert.ok(!(await veto)); + } + }); + + test('confirm onWillShutdown - veto if user cancels', async function () { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + (accessor.textFileService.models).add(model.resource, model); + + accessor.fileDialogService.setConfirmResult(ConfirmResult.CANCEL); + + await model.load(); + model.textEditorModel!.setValue('foo'); + assert.equal(accessor.textFileService.getDirty().length, 1); + + const event = new BeforeShutdownEventImpl(); + accessor.lifecycleService.fireWillShutdown(event); + assert.ok(event.value); + }); + + test('confirm onWillShutdown - no veto and backups cleaned up if user does not want to save (hot.exit: off)', async function () { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + (accessor.textFileService.models).add(model.resource, model); + + accessor.fileDialogService.setConfirmResult(ConfirmResult.DONT_SAVE); + accessor.filesConfigurationService.onFilesConfigurationChange({ files: { hotExit: 'off' } }); + + await model.load(); + model.textEditorModel!.setValue('foo'); + assert.equal(accessor.textFileService.getDirty().length, 1); + const event = new BeforeShutdownEventImpl(); + accessor.lifecycleService.fireWillShutdown(event); + + let veto = event.value; + if (typeof veto === 'boolean') { + assert.ok(accessor.backupFileService.didDiscardAllWorkspaceBackups); + assert.ok(!veto); + return; + } + + veto = await veto; + assert.ok(accessor.backupFileService.didDiscardAllWorkspaceBackups); + assert.ok(!veto); + }); + + test('confirm onWillShutdown - save (hot.exit: off)', async function () { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + (accessor.textFileService.models).add(model.resource, model); + + accessor.fileDialogService.setConfirmResult(ConfirmResult.SAVE); + accessor.filesConfigurationService.onFilesConfigurationChange({ files: { hotExit: 'off' } }); + + await model.load(); + model.textEditorModel!.setValue('foo'); + assert.equal(accessor.textFileService.getDirty().length, 1); + const event = new BeforeShutdownEventImpl(); + accessor.lifecycleService.fireWillShutdown(event); + + const veto = await (>event.value); + assert.ok(!veto); + assert.ok(!model.isDirty()); + }); + + suite('Hot Exit', () => { + suite('"onExit" setting', () => { + test('should hot exit on non-Mac (reason: CLOSE, windows: single, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, false, true, !!platform.isMacintosh); + }); + test('should hot exit on non-Mac (reason: CLOSE, windows: single, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, false, false, !!platform.isMacintosh); + }); + test('should NOT hot exit (reason: CLOSE, windows: multiple, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, true, true, true); + }); + test('should NOT hot exit (reason: CLOSE, windows: multiple, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, true, false, true); + }); + test('should hot exit (reason: QUIT, windows: single, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, false, true, false); + }); + test('should hot exit (reason: QUIT, windows: single, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, false, false, false); + }); + test('should hot exit (reason: QUIT, windows: multiple, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, true, true, false); + }); + test('should hot exit (reason: QUIT, windows: multiple, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, true, false, false); + }); + test('should hot exit (reason: RELOAD, windows: single, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, false, true, false); + }); + test('should hot exit (reason: RELOAD, windows: single, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, false, false, false); + }); + test('should hot exit (reason: RELOAD, windows: multiple, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, true, true, false); + }); + test('should hot exit (reason: RELOAD, windows: multiple, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, true, false, false); + }); + test('should NOT hot exit (reason: LOAD, windows: single, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, false, true, true); + }); + test('should NOT hot exit (reason: LOAD, windows: single, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, false, false, true); + }); + test('should NOT hot exit (reason: LOAD, windows: multiple, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, true, true, true); + }); + test('should NOT hot exit (reason: LOAD, windows: multiple, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, true, false, true); + }); + }); + + suite('"onExitAndWindowClose" setting', () => { + test('should hot exit (reason: CLOSE, windows: single, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, false, true, false); + }); + test('should hot exit (reason: CLOSE, windows: single, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, false, false, !!platform.isMacintosh); + }); + test('should hot exit (reason: CLOSE, windows: multiple, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, true, true, false); + }); + test('should NOT hot exit (reason: CLOSE, windows: multiple, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, true, false, true); + }); + test('should hot exit (reason: QUIT, windows: single, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, false, true, false); + }); + test('should hot exit (reason: QUIT, windows: single, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, false, false, false); + }); + test('should hot exit (reason: QUIT, windows: multiple, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, true, true, false); + }); + test('should hot exit (reason: QUIT, windows: multiple, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, true, false, false); + }); + test('should hot exit (reason: RELOAD, windows: single, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, false, true, false); + }); + test('should hot exit (reason: RELOAD, windows: single, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, false, false, false); + }); + test('should hot exit (reason: RELOAD, windows: multiple, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, true, true, false); + }); + test('should hot exit (reason: RELOAD, windows: multiple, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, true, false, false); + }); + test('should hot exit (reason: LOAD, windows: single, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, false, true, false); + }); + test('should NOT hot exit (reason: LOAD, windows: single, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, false, false, true); + }); + test('should hot exit (reason: LOAD, windows: multiple, workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, true, true, false); + }); + test('should NOT hot exit (reason: LOAD, windows: multiple, empty workspace)', function () { + return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, true, false, true); + }); + }); + + async function hotExitTest(this: any, setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: boolean, shouldVeto: boolean): Promise { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + (accessor.textFileService.models).add(model.resource, model); + + // Set hot exit config + accessor.filesConfigurationService.onFilesConfigurationChange({ files: { hotExit: setting } }); + + // Set empty workspace if required + if (!workspace) { + accessor.contextService.setWorkspace(new Workspace('empty:1508317022751')); + } + + // Set multiple windows if required + if (multipleWindows) { + accessor.electronService.windowCount = Promise.resolve(2); + } + + // Set cancel to force a veto if hot exit does not trigger + accessor.fileDialogService.setConfirmResult(ConfirmResult.CANCEL); + + await model.load(); + model.textEditorModel!.setValue('foo'); + assert.equal(accessor.textFileService.getDirty().length, 1); + + const event = new BeforeShutdownEventImpl(); + event.reason = shutdownReason; + accessor.lifecycleService.fireWillShutdown(event); + + const veto = await (>event.value); + assert.ok(!accessor.backupFileService.didDiscardAllWorkspaceBackups); // When hot exit is set, backups should never be cleaned since the confirm result is cancel + assert.equal(veto, shouldVeto); + } + }); +}); diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts index e3fa6b8c44e..ed9707995e9 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts @@ -86,7 +86,6 @@ suite('BackupRestorer', () => { dispose(disposables); disposables = []; - (accessor.textFileService.models).clear(); (accessor.textFileService.models).dispose(); accessor.untitledTextEditorService.revertAll(); diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts index 945cb8352de..533280f5c3b 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts @@ -92,7 +92,6 @@ suite('BackupTracker', () => { dispose(disposables); disposables = []; - (accessor.textFileService.models).clear(); (accessor.textFileService.models).dispose(); accessor.untitledTextEditorService.revertAll(); diff --git a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts index 6f7de36e6b3..8d3f2c20ef1 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts @@ -170,6 +170,10 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel this.updateContentChanged(); } + public hasBackup(): boolean { + return true; //TODO@matt forward to extension + } + public async backup(): Promise { //TODO@matt forward to extension } diff --git a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts index 68ba4c0060d..8bdc754410f 100644 --- a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts @@ -101,7 +101,6 @@ suite('EditorAutoSave', () => { part.dispose(); editorAutoSave.dispose(); - (accessor.textFileService.models).clear(); (accessor.textFileService.models).dispose(); }); diff --git a/src/vs/workbench/contrib/files/test/browser/fileEditorTracker.test.ts b/src/vs/workbench/contrib/files/test/browser/fileEditorTracker.test.ts index f0362d48c0f..d69f586eee0 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileEditorTracker.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileEditorTracker.test.ts @@ -79,7 +79,6 @@ suite('Files - FileEditorTracker', () => { assert.equal(snapshotToString(model.createSnapshot()!), 'Hello Html'); tracker.dispose(); - (accessor.textFileService.models).clear(); (accessor.textFileService.models).dispose(); }); @@ -120,7 +119,6 @@ suite('Files - FileEditorTracker', () => { part.dispose(); tracker.dispose(); - (accessor.textFileService.models).clear(); (accessor.textFileService.models).dispose(); }); diff --git a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts index 6ef4e3b0b91..3e64ab18e6e 100644 --- a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts +++ b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts @@ -7,7 +7,6 @@ import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/ import { ITextFileService, IResourceEncodings, IResourceEncoding, ModelState } from 'vs/workbench/services/textfile/common/textfiles'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; export class BrowserTextFileService extends AbstractTextFileService { @@ -17,49 +16,21 @@ export class BrowserTextFileService extends AbstractTextFileService { } }; - protected onBeforeShutdown(reason: ShutdownReason): boolean { - // Web: we cannot perform long running in the shutdown phase - // As such we need to check sync if there are any dirty files - // that have not been backed up yet and then prevent the shutdown - // if that is the case. - return this.doBeforeShutdownSync(); + protected registerListeners(): void { + super.registerListeners(); + + // Lifecycle + this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(event.reason))); } - private doBeforeShutdownSync(): boolean { + protected onBeforeShutdown(reason: ShutdownReason): boolean { if (this.models.getAll().some(model => model.hasState(ModelState.PENDING_SAVE))) { + console.warn('Unload prevented: pending file saves'); + return true; // files are pending to be saved: veto } - const dirtyResources = this.getDirty(); - if (!dirtyResources.length) { - return false; // no dirty: no veto - } - - if (!this.filesConfigurationService.isHotExitEnabled) { - return true; // dirty without backup: veto - } - - for (const dirtyResource of dirtyResources) { - let hasBackup = false; - - if (this.fileService.canHandleResource(dirtyResource)) { - const model = this.models.get(dirtyResource); - hasBackup = !!(model?.hasBackup()); - } else if (dirtyResource.scheme === Schemas.untitled) { - hasBackup = this.untitledTextEditorService.hasBackup(dirtyResource); - } - - if (!hasBackup) { - console.warn('Unload prevented: pending backups'); - return true; // dirty without backup: veto - } - } - - return false; // dirty with backups: no veto - } - - protected async getWindowCount(): Promise { - return 1; // web: we only track 1 window, not multiple + return false; } } diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 8a6c4157362..edf1aac01d9 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -7,12 +7,10 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { Emitter, AsyncEmitter } from 'vs/base/common/event'; import * as platform from 'vs/base/common/platform'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, ITextFileEditorModel, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, FileOperationWillRunEvent, FileOperationDidRunEvent, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { IRevertOptions, IEncodingSupport } from 'vs/workbench/common/editor'; -import { ILifecycleService, ShutdownReason, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IFileService, FileOperationError, FileOperationResult, HotExitConfiguration, IFileStatWithMetadata, ICreateFileOptions, FileOperation } from 'vs/platform/files/common/files'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IFileService, FileOperationError, FileOperationResult, IFileStatWithMetadata, ICreateFileOptions, FileOperation } from 'vs/platform/files/common/files'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; @@ -24,9 +22,8 @@ import { Schemas } from 'vs/base/common/network'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { createTextBufferFactoryFromSnapshot, createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { isEqualOrParent, isEqual, joinPath, dirname, extname, basename, toLocalResource } from 'vs/base/common/resources'; -import { IDialogService, IFileDialogService, ISaveDialogOptions, IConfirmation, ConfirmResult } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService, IFileDialogService, ISaveDialogOptions, IConfirmation } from 'vs/platform/dialogs/common/dialogs'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { coalesce } from 'vs/base/common/arrays'; @@ -35,7 +32,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { ITextSnapshot } from 'vs/editor/common/model'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; -import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; @@ -61,16 +58,13 @@ export abstract class AbstractTextFileService extends Disposable implements ITex abstract get encoding(): IResourceEncodings; constructor( - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IFileService protected readonly fileService: IFileService, @IUntitledTextEditorService protected readonly untitledTextEditorService: IUntitledTextEditorService, - @ILifecycleService private readonly lifecycleService: ILifecycleService, + @ILifecycleService protected readonly lifecycleService: ILifecycleService, @IInstantiationService protected readonly instantiationService: IInstantiationService, @IModeService private readonly modeService: IModeService, @IModelService private readonly modelService: IModelService, @IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService, - @INotificationService private readonly notificationService: INotificationService, - @IBackupFileService private readonly backupFileService: IBackupFileService, @IHistoryService private readonly historyService: IHistoryService, @IDialogService private readonly dialogService: IDialogService, @IFileDialogService private readonly fileDialogService: IFileDialogService, @@ -84,196 +78,12 @@ export abstract class AbstractTextFileService extends Disposable implements ITex this.registerListeners(); } - private registerListeners(): void { + protected registerListeners(): void { // Lifecycle - this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(event.reason))); this.lifecycleService.onShutdown(this.dispose, this); } - //#region shutdown / backup handling - - protected onBeforeShutdown(reason: ShutdownReason): boolean | Promise { - - // Dirty files need treatment on shutdown - const dirty = this.getDirty(); - if (dirty.length) { - - // If auto save is enabled, save all files and then check again for dirty files - // We DO NOT run any save participant if we are in the shutdown phase for performance reasons - if (this.filesConfigurationService.getAutoSaveMode() !== AutoSaveMode.OFF) { - return this.saveAll(false /* files only */, { skipSaveParticipants: true }).then(() => { - - // If we still have dirty files, we either have untitled ones or files that cannot be saved - const remainingDirty = this.getDirty(); - if (remainingDirty.length) { - return this.handleDirtyBeforeShutdown(remainingDirty, reason); - } - - return false; - }); - } - - // Auto save is not enabled - return this.handleDirtyBeforeShutdown(dirty, reason); - } - - // No dirty files: no veto - return this.noVeto({ cleanUpBackups: true }); - } - - private handleDirtyBeforeShutdown(dirty: URI[], reason: ShutdownReason): boolean | Promise { - - // If hot exit is enabled, backup dirty files and allow to exit without confirmation - if (this.filesConfigurationService.isHotExitEnabled) { - return this.backupBeforeShutdown(dirty, reason).then(didBackup => { - if (didBackup) { - return this.noVeto({ cleanUpBackups: false }); // no veto and no backup cleanup (since backup was successful) - } - - // since a backup did not happen, we have to confirm for the dirty files now - return this.confirmBeforeShutdown(); - }, error => { - this.notificationService.error(nls.localize('files.backup.failSave', "Files that are dirty could not be written to the backup location (Error: {0}). Try saving your files first and then exit.", error.message)); - - return true; // veto, the backups failed - }); - } - - // Otherwise just confirm from the user what to do with the dirty files - return this.confirmBeforeShutdown(); - } - - private async backupBeforeShutdown(dirtyToBackup: URI[], reason: ShutdownReason): Promise { - // When quit is requested skip the confirm callback and attempt to backup all workspaces. - // When quit is not requested the confirm callback should be shown when the window being - // closed is the only VS Code window open, except for on Mac where hot exit is only - // ever activated when quit is requested. - - let doBackup: boolean | undefined; - switch (reason) { - case ShutdownReason.CLOSE: - if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.filesConfigurationService.hotExitConfiguration === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) { - doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured - } else if (await this.getWindowCount() > 1 || platform.isMacintosh) { - doBackup = false; // do not backup if a window is closed that does not cause quitting of the application - } else { - doBackup = true; // backup if last window is closed on win/linux where the application quits right after - } - break; - - case ShutdownReason.QUIT: - doBackup = true; // backup because next start we restore all backups - break; - - case ShutdownReason.RELOAD: - doBackup = true; // backup because after window reload, backups restore - break; - - case ShutdownReason.LOAD: - if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.filesConfigurationService.hotExitConfiguration === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) { - doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured - } else { - doBackup = false; // do not backup because we are switching contexts - } - break; - } - - if (!doBackup) { - return false; - } - - await this.backupAll(dirtyToBackup); - - return true; - } - - protected abstract getWindowCount(): Promise; - - private backupAll(dirtyToBackup: URI[]): Promise { - - // split up between files and untitled - const filesToBackup: ITextFileEditorModel[] = []; - const untitledToBackup: URI[] = []; - dirtyToBackup.forEach(dirty => { - if (this.fileService.canHandleResource(dirty)) { - const model = this.models.get(dirty); - if (model) { - filesToBackup.push(model); - } - } else if (dirty.scheme === Schemas.untitled) { - untitledToBackup.push(dirty); - } - }); - - return this.doBackupAll(filesToBackup, untitledToBackup); - } - - private async doBackupAll(dirtyFileModels: ITextFileEditorModel[], untitledResources: URI[]): Promise { - - // Handle file resources first - await Promise.all(dirtyFileModels.map(model => model.backup())); - - // Handle untitled resources - await Promise.all(untitledResources - .filter(untitled => this.untitledTextEditorService.exists(untitled)) - .map(async untitled => (await this.untitledTextEditorService.createOrGet({ resource: untitled }).resolve()).backup())); - } - - private async confirmBeforeShutdown(): Promise { - const confirm = await this.fileDialogService.showSaveConfirm(this.getDirty()); - - // Save - if (confirm === ConfirmResult.SAVE) { - const result = await this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true }); - - if (result.results.some(r => r.error)) { - return true; // veto if some saves failed - } - - return this.noVeto({ cleanUpBackups: true }); - } - - // Don't Save - else if (confirm === ConfirmResult.DONT_SAVE) { - - // Make sure to revert untitled so that they do not restore - // see https://github.com/Microsoft/vscode/issues/29572 - this.untitledTextEditorService.revertAll(); - - return this.noVeto({ cleanUpBackups: true }); - } - - // Cancel - else if (confirm === ConfirmResult.CANCEL) { - return true; // veto - } - - return false; - } - - private noVeto(options: { cleanUpBackups: boolean }): boolean | Promise { - if (!options.cleanUpBackups) { - return false; - } - - if (this.lifecycleService.phase < LifecyclePhase.Restored) { - return false; // if editors have not restored, we are not up to speed with backups and thus should not clean them - } - - return this.cleanupBackupsBeforeShutdown().then(() => false, () => false); - } - - protected async cleanupBackupsBeforeShutdown(): Promise { - if (this.environmentService.isExtensionDevelopment) { - return; - } - - await this.backupFileService.discardAllWorkspaceBackups(); - } - - //#endregion - //#region text file IO primitives (read, create, move, delete, update) async read(resource: URI, options?: IReadTextFileOptions): Promise { @@ -903,6 +713,8 @@ export abstract class AbstractTextFileService extends Disposable implements ITex //#endregion + //#region dirty + getDirty(resources?: URI[]): URI[] { // Collect files @@ -924,4 +736,6 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // Check for dirty untitled return this.untitledTextEditorService.getDirty().some(dirty => !resource || dirty.toString() === resource.toString()); } + + //#endregion } diff --git a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts index 17eeb9d9228..b9eca7404e9 100644 --- a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts @@ -27,15 +27,12 @@ import { Readable } from 'stream'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; import { ITextSnapshot } from 'vs/editor/common/model'; import { nodeReadableToString, streamToNodeReadable, nodeStreamToVSBufferReadable } from 'vs/base/node/stream'; -import { IElectronService } from 'vs/platform/electron/node/electron'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -46,7 +43,6 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; export class NativeTextFileService extends AbstractTextFileService { constructor( - @IWorkspaceContextService contextService: IWorkspaceContextService, @IFileService fileService: IFileService, @IUntitledTextEditorService untitledTextEditorService: IUntitledTextEditorService, @ILifecycleService lifecycleService: ILifecycleService, @@ -54,19 +50,16 @@ export class NativeTextFileService extends AbstractTextFileService { @IModeService modeService: IModeService, @IModelService modelService: IModelService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, - @INotificationService notificationService: INotificationService, - @IBackupFileService backupFileService: IBackupFileService, @IHistoryService historyService: IHistoryService, @IDialogService dialogService: IDialogService, @IFileDialogService fileDialogService: IFileDialogService, @IEditorService editorService: IEditorService, @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, - @IElectronService private readonly electronService: IElectronService, @IProductService private readonly productService: IProductService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, @ITextModelService textModelService: ITextModelService ) { - super(contextService, fileService, untitledTextEditorService, lifecycleService, instantiationService, modeService, modelService, environmentService, notificationService, backupFileService, historyService, dialogService, fileDialogService, editorService, textResourceConfigurationService, filesConfigurationService, textModelService); + super(fileService, untitledTextEditorService, lifecycleService, instantiationService, modeService, modelService, environmentService, historyService, dialogService, fileDialogService, editorService, textResourceConfigurationService, filesConfigurationService, textModelService); } private _encoding: EncodingOracle | undefined; @@ -312,10 +305,6 @@ export class NativeTextFileService extends AbstractTextFileService { }); }); } - - protected getWindowCount(): Promise { - return this.electronService.getWindowCount(); - } } export interface IEncodingOverride { diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index 4fac27a7c43..ccf2844c238 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -47,7 +47,7 @@ suite('Files - TextFileEditorModel', () => { }); teardown(() => { - (accessor.textFileService.models).clear(); + (accessor.textFileService.models).dispose(); TextFileEditorModel.setSaveParticipant(null); // reset any set participant accessor.fileService.setContent(content); }); diff --git a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts index eae7fcd7217..9b882d978e3 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts @@ -94,7 +94,6 @@ suite('Files - TextFileService i/o', () => { }); teardown(async () => { - (accessor.textFileService.models).clear(); (accessor.textFileService.models).dispose(); accessor.untitledTextEditorService.revertAll(); diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts index 1c04252cdd7..4358e4dbc3f 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -4,24 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as sinon from 'sinon'; -import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { ILifecycleService, BeforeShutdownEvent, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { workbenchInstantiationService, TestLifecycleService, TestTextFileService, TestContextService, TestFileService, TestElectronService, TestFilesConfigurationService, TestFileDialogService } from 'vs/workbench/test/workbenchTestServices'; import { toResource } from 'vs/base/test/common/utils'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; -import { HotExitConfiguration, IFileService } from 'vs/platform/files/common/files'; +import { IFileService } from 'vs/platform/files/common/files'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; -import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { Schemas } from 'vs/base/common/network'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs'; +import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; class ServiceAccessor { constructor( @@ -38,16 +37,6 @@ class ServiceAccessor { } } -class BeforeShutdownEventImpl implements BeforeShutdownEvent { - - value: boolean | Promise | undefined; - reason = ShutdownReason.CLOSE; - - veto(value: boolean | Promise): void { - this.value = value; - } -} - suite('Files - TextFileService', () => { let instantiationService: IInstantiationService; @@ -63,87 +52,10 @@ suite('Files - TextFileService', () => { if (model) { model.dispose(); } - (accessor.textFileService.models).clear(); (accessor.textFileService.models).dispose(); accessor.untitledTextEditorService.revertAll(); }); - test('confirm onWillShutdown - no veto', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); - (accessor.textFileService.models).add(model.resource, model); - - const event = new BeforeShutdownEventImpl(); - accessor.lifecycleService.fireWillShutdown(event); - - const veto = event.value; - if (typeof veto === 'boolean') { - assert.ok(!veto); - } else { - assert.ok(!(await veto)); - } - }); - - test('confirm onWillShutdown - veto if user cancels', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); - (accessor.textFileService.models).add(model.resource, model); - - const service = accessor.textFileService; - accessor.fileDialogService.setConfirmResult(ConfirmResult.CANCEL); - - await model.load(); - model.textEditorModel!.setValue('foo'); - assert.equal(service.getDirty().length, 1); - - const event = new BeforeShutdownEventImpl(); - accessor.lifecycleService.fireWillShutdown(event); - assert.ok(event.value); - }); - - test('confirm onWillShutdown - no veto and backups cleaned up if user does not want to save (hot.exit: off)', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); - (accessor.textFileService.models).add(model.resource, model); - - const service = accessor.textFileService; - accessor.fileDialogService.setConfirmResult(ConfirmResult.DONT_SAVE); - accessor.filesConfigurationService.onFilesConfigurationChange({ files: { hotExit: 'off' } }); - - await model.load(); - model.textEditorModel!.setValue('foo'); - assert.equal(service.getDirty().length, 1); - const event = new BeforeShutdownEventImpl(); - accessor.lifecycleService.fireWillShutdown(event); - - let veto = event.value; - if (typeof veto === 'boolean') { - assert.ok(service.cleanupBackupsBeforeShutdownCalled); - assert.ok(!veto); - return; - } - - veto = await veto; - assert.ok(service.cleanupBackupsBeforeShutdownCalled); - assert.ok(!veto); - }); - - test('confirm onWillShutdown - save (hot.exit: off)', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); - (accessor.textFileService.models).add(model.resource, model); - - const service = accessor.textFileService; - accessor.fileDialogService.setConfirmResult(ConfirmResult.SAVE); - accessor.filesConfigurationService.onFilesConfigurationChange({ files: { hotExit: 'off' } }); - - await model.load(); - model.textEditorModel!.setValue('foo'); - assert.equal(service.getDirty().length, 1); - const event = new BeforeShutdownEventImpl(); - accessor.lifecycleService.fireWillShutdown(event); - - const veto = await (>event.value); - assert.ok(!veto); - assert.ok(!model.isDirty()); - }); - test('isDirty/getDirty - files and untitled', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.resource, model); @@ -310,138 +222,4 @@ suite('Files - TextFileService', () => { sourceModel.dispose(); targetModel.dispose(); } - - suite('Hot Exit', () => { - suite('"onExit" setting', () => { - test('should hot exit on non-Mac (reason: CLOSE, windows: single, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, false, true, !!platform.isMacintosh); - }); - test('should hot exit on non-Mac (reason: CLOSE, windows: single, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, false, false, !!platform.isMacintosh); - }); - test('should NOT hot exit (reason: CLOSE, windows: multiple, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, true, true, true); - }); - test('should NOT hot exit (reason: CLOSE, windows: multiple, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.CLOSE, true, false, true); - }); - test('should hot exit (reason: QUIT, windows: single, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, false, true, false); - }); - test('should hot exit (reason: QUIT, windows: single, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, false, false, false); - }); - test('should hot exit (reason: QUIT, windows: multiple, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, true, true, false); - }); - test('should hot exit (reason: QUIT, windows: multiple, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.QUIT, true, false, false); - }); - test('should hot exit (reason: RELOAD, windows: single, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, false, true, false); - }); - test('should hot exit (reason: RELOAD, windows: single, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, false, false, false); - }); - test('should hot exit (reason: RELOAD, windows: multiple, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, true, true, false); - }); - test('should hot exit (reason: RELOAD, windows: multiple, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.RELOAD, true, false, false); - }); - test('should NOT hot exit (reason: LOAD, windows: single, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, false, true, true); - }); - test('should NOT hot exit (reason: LOAD, windows: single, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, false, false, true); - }); - test('should NOT hot exit (reason: LOAD, windows: multiple, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, true, true, true); - }); - test('should NOT hot exit (reason: LOAD, windows: multiple, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT, ShutdownReason.LOAD, true, false, true); - }); - }); - - suite('"onExitAndWindowClose" setting', () => { - test('should hot exit (reason: CLOSE, windows: single, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, false, true, false); - }); - test('should hot exit (reason: CLOSE, windows: single, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, false, false, !!platform.isMacintosh); - }); - test('should hot exit (reason: CLOSE, windows: multiple, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, true, true, false); - }); - test('should NOT hot exit (reason: CLOSE, windows: multiple, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.CLOSE, true, false, true); - }); - test('should hot exit (reason: QUIT, windows: single, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, false, true, false); - }); - test('should hot exit (reason: QUIT, windows: single, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, false, false, false); - }); - test('should hot exit (reason: QUIT, windows: multiple, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, true, true, false); - }); - test('should hot exit (reason: QUIT, windows: multiple, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.QUIT, true, false, false); - }); - test('should hot exit (reason: RELOAD, windows: single, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, false, true, false); - }); - test('should hot exit (reason: RELOAD, windows: single, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, false, false, false); - }); - test('should hot exit (reason: RELOAD, windows: multiple, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, true, true, false); - }); - test('should hot exit (reason: RELOAD, windows: multiple, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.RELOAD, true, false, false); - }); - test('should hot exit (reason: LOAD, windows: single, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, false, true, false); - }); - test('should NOT hot exit (reason: LOAD, windows: single, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, false, false, true); - }); - test('should hot exit (reason: LOAD, windows: multiple, workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, true, true, false); - }); - test('should NOT hot exit (reason: LOAD, windows: multiple, empty workspace)', function () { - return hotExitTest.call(this, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE, ShutdownReason.LOAD, true, false, true); - }); - }); - - async function hotExitTest(this: any, setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: boolean, shouldVeto: boolean): Promise { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); - (accessor.textFileService.models).add(model.resource, model); - - const service = accessor.textFileService; - // Set hot exit config - accessor.filesConfigurationService.onFilesConfigurationChange({ files: { hotExit: setting } }); - // Set empty workspace if required - if (!workspace) { - accessor.contextService.setWorkspace(new Workspace('empty:1508317022751')); - } - // Set multiple windows if required - if (multipleWindows) { - accessor.electronService.windowCount = Promise.resolve(2); - } - // Set cancel to force a veto if hot exit does not trigger - accessor.fileDialogService.setConfirmResult(ConfirmResult.CANCEL); - - await model.load(); - model.textEditorModel!.setValue('foo'); - assert.equal(service.getDirty().length, 1); - const event = new BeforeShutdownEventImpl(); - event.reason = shutdownReason; - accessor.lifecycleService.fireWillShutdown(event); - - const veto = await (>event.value); - assert.ok(!service.cleanupBackupsBeforeShutdownCalled); // When hot exit is set, backups should never be cleaned since the confirm result is cancel - assert.equal(veto, shouldVeto); - } - }); }); diff --git a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts index 3ffe921e4cf..516c26c69cf 100644 --- a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts +++ b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts @@ -48,7 +48,6 @@ suite('Workbench - TextModelResolverService', () => { model.dispose(); model = (undefined)!; } - (accessor.textFileService.models).clear(); (accessor.textFileService.models).dispose(); accessor.untitledTextEditorService.revertAll(); }); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index 4c12fbb093f..c3543e4c824 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -9,7 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { Disposable, IDisposable, toDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; import { TernarySearchTree, values } from 'vs/base/common/map'; -import { ISaveOptions } from 'vs/workbench/common/editor'; +import { ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor'; export const enum WorkingCopyCapabilities { @@ -48,6 +48,10 @@ export interface IWorkingCopy { save(options?: ISaveOptions): Promise; + revert(options?: IRevertOptions): Promise; + + hasBackup(): boolean; + backup(): Promise; //#endregion diff --git a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts index 908c24a2251..b94eb61e384 100644 --- a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { TestWorkingCopyService } from 'vs/workbench/test/workbenchTestServices'; -import { ISaveOptions } from 'vs/workbench/common/editor'; +import { ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor'; suite('WorkingCopyService', () => { @@ -53,8 +53,16 @@ suite('WorkingCopyService', () => { return true; } + async revert(options?: IRevertOptions): Promise { + this.setDirty(false); + + return true; + } + async backup(): Promise { } + hasBackup(): boolean { return false; } + dispose(): void { this._onDispose.fire(); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts index 40b3489c80f..62a915c2535 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts @@ -33,7 +33,7 @@ suite('MainThreadSaveParticipant', function () { }); teardown(() => { - (accessor.textFileService.models).clear(); + (accessor.textFileService.models).dispose(); TextFileEditorModel.setSaveParticipant(null); // reset any set participant }); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index c9b8e6f48d7..c048560e2d5 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -190,13 +190,10 @@ export class TestContextService implements IWorkspaceContextService { } export class TestTextFileService extends NativeTextFileService { - cleanupBackupsBeforeShutdownCalled!: boolean; - private promptPath!: URI; private resolveTextContentError!: FileOperationError | null; constructor( - @IWorkspaceContextService contextService: IWorkspaceContextService, @IFileService protected fileService: IFileService, @IUntitledTextEditorService untitledTextEditorService: IUntitledTextEditorService, @ILifecycleService lifecycleService: ILifecycleService, @@ -204,20 +201,16 @@ export class TestTextFileService extends NativeTextFileService { @IModeService modeService: IModeService, @IModelService modelService: IModelService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, - @INotificationService notificationService: INotificationService, - @IBackupFileService backupFileService: IBackupFileService, @IHistoryService historyService: IHistoryService, @IDialogService dialogService: IDialogService, @IFileDialogService fileDialogService: IFileDialogService, @IEditorService editorService: IEditorService, @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, - @IElectronService electronService: IElectronService, @IProductService productService: IProductService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, @ITextModelService textModelService: ITextModelService ) { super( - contextService, fileService, untitledTextEditorService, lifecycleService, @@ -225,14 +218,11 @@ export class TestTextFileService extends NativeTextFileService { modeService, modelService, environmentService, - notificationService, - backupFileService, historyService, dialogService, fileDialogService, editorService, textResourceConfigurationService, - electronService, productService, filesConfigurationService, textModelService @@ -271,11 +261,6 @@ export class TestTextFileService extends NativeTextFileService { promptForPath(_resource: URI, _defaultPath: URI): Promise { return Promise.resolve(this.promptPath); } - - protected cleanupBackupsBeforeShutdown(): Promise { - this.cleanupBackupsBeforeShutdownCalled = true; - return Promise.resolve(); - } } export interface ITestInstantiationService extends IInstantiationService { @@ -1225,7 +1210,11 @@ export class TestBackupFileService implements IBackupFileService { return Promise.resolve(); } + didDiscardAllWorkspaceBackups = false; + discardAllWorkspaceBackups(): Promise { + this.didDiscardAllWorkspaceBackups = true; + return Promise.resolve(); } } diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index f303692242f..bd8b76dad8f 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -92,6 +92,9 @@ import 'vs/workbench/contrib/splash/electron-browser/partsSplash.contribution'; import 'vs/workbench/contrib/files/electron-browser/files.contribution'; import 'vs/workbench/contrib/files/electron-browser/fileActions.contribution'; +// Backup +import 'vs/workbench/contrib/backup/electron-browser/backup.contribution'; + // Debug import 'vs/workbench/contrib/debug/node/debugHelperService'; import 'vs/workbench/contrib/debug/electron-browser/extensionHostDebugService'; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index ecd9ff04e17..e6c3807eeff 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -91,6 +91,9 @@ registerSingleton(IUserDataSyncService, UserDataSyncService); // Explorer import 'vs/workbench/contrib/files/browser/files.web.contribution'; +// Backup +import 'vs/workbench/contrib/backup/browser/backup.web.contribution'; + // Preferences import 'vs/workbench/contrib/preferences/browser/keyboardLayoutPicker'; From 3df58b02801a92135f2f91649f765025912e6bbf Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 14 Jan 2020 16:06:00 +0100 Subject: [PATCH 263/843] debug: use new view pane in debug console --- .../debug/browser/debug.contribution.ts | 50 +++++++--------- .../contrib/debug/browser/debugCommands.ts | 10 ++-- .../debug/browser/debugEditorActions.ts | 8 +-- .../contrib/debug/browser/debugService.ts | 10 ++-- .../contrib/debug/browser/debugViewlet.ts | 22 +++---- .../workbench/contrib/debug/browser/repl.ts | 59 +++++++++---------- .../workbench/contrib/debug/common/debug.ts | 3 +- 7 files changed, 80 insertions(+), 82 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 3874102dd89..061cd5dcd61 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -14,16 +14,14 @@ import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry' import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionRegistryExtensions } from 'vs/workbench/common/actions'; import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; -import { TogglePanelAction, Extensions as PanelExtensions, PanelRegistry, PanelDescriptor } from 'vs/workbench/browser/panel'; import { BreakpointsView } from 'vs/workbench/contrib/debug/browser/breakpointsView'; import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { - IDebugService, VIEWLET_ID, REPL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA, - CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, + IDebugService, VIEWLET_ID, DEBUG_PANEL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA, + CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, } from 'vs/workbench/contrib/debug/common/debug'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { StartAction, AddFunctionBreakpointAction, ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar'; import * as service from 'vs/workbench/contrib/debug/browser/debugService'; @@ -31,7 +29,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { registerCommands, ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_ID, REVERSE_CONTINUE_ID, STEP_BACK_ID, RESTART_SESSION_ID, TERMINATE_THREAD_ID, STEP_OVER_ID, STEP_INTO_ID, STEP_OUT_ID, PAUSE_ID, DISCONNECT_ID, STOP_ID, RESTART_FRAME_ID, CONTINUE_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, RESTART_LABEL, STEP_INTO_LABEL, STEP_OVER_LABEL, STEP_OUT_LABEL, PAUSE_LABEL, DISCONNECT_LABEL, STOP_LABEL, CONTINUE_LABEL } from 'vs/workbench/contrib/debug/browser/debugCommands'; import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { StatusBarColorProvider } from 'vs/workbench/contrib/debug/browser/statusbarColorProvider'; -import { IViewsRegistry, Extensions as ViewExtensions, IViewContainersRegistry, ViewContainerLocation } from 'vs/workbench/common/views'; +import { IViewsRegistry, Extensions as ViewExtensions, IViewContainersRegistry, ViewContainerLocation, ViewContainer } from 'vs/workbench/common/views'; import { isMacintosh } from 'vs/base/common/platform'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { URI } from 'vs/base/common/uri'; @@ -48,11 +46,12 @@ import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl'; import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; import { StartView } from 'vs/workbench/contrib/debug/browser/startView'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { DebugViewPaneContainer } from 'vs/workbench/contrib/debug/browser/debugViewlet'; +import { DebugViewPaneContainer, OpenDebugPanelAction } from 'vs/workbench/contrib/debug/browser/debugViewlet'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { CallStackEditorContribution } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution'; import { BreakpointEditorContribution } from 'vs/workbench/contrib/debug/browser/breakpointEditorContribution'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; class OpenDebugViewletAction extends ShowViewletAction { public static readonly ID = VIEWLET_ID; @@ -69,20 +68,6 @@ class OpenDebugViewletAction extends ShowViewletAction { } } -class OpenDebugPanelAction extends TogglePanelAction { - public static readonly ID = 'workbench.debug.action.toggleRepl'; - public static readonly LABEL = nls.localize('toggleDebugPanel', "Debug Console"); - - constructor( - id: string, - label: string, - @IPanelService panelService: IPanelService, - @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService - ) { - super(id, label, REPL_ID, panelService, layoutService); - } -} - const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, name: nls.localize('runAndDebug', "Run and Debug"), @@ -99,14 +84,23 @@ const openPanelKb: IKeybindings = { }; // register repl panel -Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create( - Repl, - REPL_ID, - nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), - 'repl', - 30, - OpenDebugPanelAction.ID -)); + +const VIEW_CONTAINER: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ + id: DEBUG_PANEL_ID, + name: nls.localize('runAndDebug', "Run and Debug"), + ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), + focusCommand: { + id: OpenDebugPanelAction.ID, + keybindings: openPanelKb + } +}, ViewContainerLocation.Panel); + +Registry.as(ViewExtensions.ViewsRegistry).registerViews([{ + id: REPL_VIEW_ID, + name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), + canToggleVisibility: false, + ctorDescriptor: new SyncDescriptor(Repl), +}], VIEW_CONTAINER); // Register default debug views const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 7ea1d5ed7cf..d65a6f30be2 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -9,7 +9,7 @@ import { List } from 'vs/base/browser/ui/list/listWidget'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IListService } from 'vs/platform/list/browser/listService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, REPL_ID, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, REPL_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; import { Expression, Variable, Breakpoint, FunctionBreakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -27,9 +27,9 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IViewsService } from 'vs/workbench/common/views'; export const ADD_CONFIGURATION_ID = 'debug.addConfiguration'; export const TOGGLE_INLINE_BREAKPOINT_ID = 'editor.debug.action.toggleInlineBreakpoint'; @@ -327,9 +327,9 @@ export function registerCommands(): void { CommandsRegistry.registerCommand({ id: FOCUS_REPL_ID, - handler: (accessor) => { - const panelService = accessor.get(IPanelService); - panelService.openPanel(REPL_ID, true); + handler: async (accessor) => { + const viewsService = accessor.get(IViewsService); + await viewsService.openView(REPL_VIEW_ID, true); } }); diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 9650fba3db7..2ca496d131c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -9,14 +9,14 @@ import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution } from 'vs/workbench/contrib/debug/common/debug'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { PanelFocusContext } from 'vs/workbench/common/panel'; +import { IViewsService } from 'vs/workbench/common/views'; export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint'; class ToggleBreakpointAction extends EditorAction { @@ -169,7 +169,7 @@ class SelectionToReplAction extends EditorAction { async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const debugService = accessor.get(IDebugService); - const panelService = accessor.get(IPanelService); + const viewsService = accessor.get(IViewsService); const viewModel = debugService.getViewModel(); const session = viewModel.focusedSession; if (!editor.hasModel() || !session) { @@ -178,7 +178,7 @@ class SelectionToReplAction extends EditorAction { const text = editor.getModel().getValueInRange(editor.getSelection()); await session.addReplExpression(viewModel.focusedStackFrame!, text); - await panelService.openPanel(REPL_ID, true); + await viewsService.openView(REPL_VIEW_ID, true); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 5f805dd778b..77fd7a3ab3b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -36,7 +36,7 @@ import { IAction } from 'vs/base/common/actions'; import { deepClone, equals } from 'vs/base/common/objects'; import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, DEBUG_PANEL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; import { getExtensionHostDebugSession } from 'vs/workbench/contrib/debug/common/debugUtils'; import { isErrorWithActions } from 'vs/base/common/errorsWithActions'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -45,6 +45,7 @@ import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { TaskRunResult, DebugTaskRunner } from 'vs/workbench/contrib/debug/browser/debugTaskRunner'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; +import { IViewsService } from 'vs/workbench/common/views'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint'; @@ -80,6 +81,7 @@ export class DebugService implements IDebugService { @ITextFileService private readonly textFileService: ITextFileService, @IViewletService private readonly viewletService: IViewletService, @IPanelService private readonly panelService: IPanelService, + @IViewsService private readonly viewsService: IViewsService, @INotificationService private readonly notificationService: INotificationService, @IDialogService private readonly dialogService: IDialogService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @@ -466,7 +468,7 @@ export class DebugService implements IDebugService { const internalConsoleOptions = session.configuration.internalConsoleOptions || this.configurationService.getValue('debug').internalConsoleOptions; if (internalConsoleOptions === 'openOnSessionStart' || (this.viewModel.firstSessionStart && internalConsoleOptions === 'openOnFirstSessionStart')) { - this.panelService.openPanel(REPL_ID, false); + this.viewsService.openView(REPL_VIEW_ID, false); } this.viewModel.firstSessionStart = false; @@ -492,7 +494,7 @@ export class DebugService implements IDebugService { // Show the repl if some error got logged there #5870 if (session && session.getReplElements().length > 0) { - this.panelService.openPanel(REPL_ID, false); + this.viewsService.openView(REPL_VIEW_ID, false); } if (session.configuration && session.configuration.request === 'attach' && session.configuration.__autoAttach) { @@ -577,7 +579,7 @@ export class DebugService implements IDebugService { const dataBreakpoints = this.model.getDataBreakpoints().filter(dbp => !dbp.canPersist); dataBreakpoints.forEach(dbp => this.model.removeDataBreakpoints(dbp.getId())); - if (this.panelService.getLastActivePanelId() === REPL_ID && this.configurationService.getValue('debug').console.closeOnEnd) { + if (this.panelService.getLastActivePanelId() === DEBUG_PANEL_ID && this.configurationService.getValue('debug').console.closeOnEnd) { this.panelService.hideActivePanel(); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index 5afeb4290cb..df274ebb967 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -8,7 +8,7 @@ import * as nls from 'vs/nls'; import { IAction } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IDebugService, VIEWLET_ID, State, BREAKPOINTS_VIEW_ID, IDebugConfiguration, REPL_ID, CONTEXT_DEBUG_UX, CONTEXT_DEBUG_UX_KEY } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, VIEWLET_ID, State, BREAKPOINTS_VIEW_ID, IDebugConfiguration, DEBUG_PANEL_ID, CONTEXT_DEBUG_UX, CONTEXT_DEBUG_UX_KEY } from 'vs/workbench/contrib/debug/common/debug'; import { StartAction, ConfigureAction, SelectAndStartAction, FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { StartDebugActionViewItem, FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -105,8 +105,8 @@ export class DebugViewPaneContainer extends ViewPaneContainer { } @memoize - private get toggleReplAction(): ToggleReplAction { - return this._register(this.instantiationService.createInstance(ToggleReplAction, ToggleReplAction.ID, ToggleReplAction.LABEL)); + private get toggleReplAction(): OpenDebugPanelAction { + return this._register(this.instantiationService.createInstance(OpenDebugPanelAction, OpenDebugPanelAction.ID, OpenDebugPanelAction.LABEL)); } @memoize @@ -228,14 +228,16 @@ export class DebugViewPaneContainer extends ViewPaneContainer { } } -class ToggleReplAction extends TogglePanelAction { - static readonly ID = 'debug.toggleRepl'; - static readonly LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugConsoleAction' }, 'Debug Console'); +export class OpenDebugPanelAction extends TogglePanelAction { + public static readonly ID = 'workbench.debug.action.toggleRepl'; + public static readonly LABEL = nls.localize('toggleDebugPanel', "Debug Console"); - constructor(id: string, label: string, - @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @IPanelService panelService: IPanelService + constructor( + id: string, + label: string, + @IPanelService panelService: IPanelService, + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService ) { - super(id, label, REPL_ID, panelService, layoutService, 'debug-action codicon-terminal'); + super(id, label, DEBUG_PANEL_ID, panelService, layoutService); } } diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 49af349bca6..1503ed1a983 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -18,17 +18,15 @@ import { registerEditorAction, ServicesAccessor, EditorAction } from 'vs/editor/ import { IModelService } from 'vs/editor/common/services/modelService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { Panel } from 'vs/workbench/browser/panel'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { memoize } from 'vs/base/common/decorators'; import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { IDebugService, REPL_ID, DEBUG_SCHEME, CONTEXT_IN_DEBUG_REPL, IDebugSession, State, IReplElement, IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, DEBUG_SCHEME, CONTEXT_IN_DEBUG_REPL, IDebugSession, State, IReplElement, IDebugConfiguration, REPL_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; import { HistoryNavigator } from 'vs/base/common/history'; import { IHistoryNavigationWidget } from 'vs/base/browser/history'; import { createAndBindHistoryNavigationWidgetScopedContextKeyService } from 'vs/platform/browser/contextScopedHistoryWidget'; @@ -40,7 +38,6 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; import { CompletionContext, CompletionList, CompletionProviderRegistry, CompletionItem, completionKindFromString, CompletionItemKind } from 'vs/editor/common/modes'; import { first } from 'vs/base/common/arrays'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; @@ -56,6 +53,9 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; import { ReplDelegate, ReplVariablesRenderer, ReplSimpleElementsRenderer, ReplEvaluationInputsRenderer, ReplEvaluationResultsRenderer, ReplRawObjectsRenderer, ReplDataSource, ReplAccessibilityProvider } from 'vs/workbench/contrib/debug/browser/replViewer'; import { localize } from 'vs/nls'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IViewsService } from 'vs/workbench/common/views'; const $ = dom.$; @@ -77,7 +77,8 @@ function revealLastElement(tree: WorkbenchAsyncDataTree) { } const sessionsToIgnore = new Set(); -export class Repl extends Panel implements IPrivateReplService, IHistoryNavigationWidget { + +export class Repl extends ViewPane implements IPrivateReplService, IHistoryNavigationWidget { _serviceBrand: undefined; private static readonly REFRESH_DELAY = 100; // delay in ms to refresh the repl for new elements to show @@ -100,21 +101,22 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati private modelChangeListener: IDisposable = Disposable.None; constructor( + options: IViewPaneOptions, @IDebugService private readonly debugService: IDebugService, - @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IStorageService private readonly storageService: IStorageService, @IThemeService protected themeService: IThemeService, @IModelService private readonly modelService: IModelService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @ICodeEditorService codeEditorService: ICodeEditorService, - @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IConfigurationService configurationService: IConfigurationService, @ITextResourcePropertiesService private readonly textResourcePropertiesService: ITextResourcePropertiesService, @IClipboardService private readonly clipboardService: IClipboardService, - @IEditorService private readonly editorService: IEditorService + @IEditorService private readonly editorService: IEditorService, + @IKeybindingService keybindingService: IKeybindingService ) { - super(REPL_ID, telemetryService, themeService, storageService); + super({ ...(options as IViewPaneOptions), id: REPL_VIEW_ID, ariaHeaderLabel: localize('debugConsole', "Debug Console") }, keybindingService, contextMenuService, configurationService, contextKeyService); this.history = new HistoryNavigator(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')), 50); codeEditorService.registerDecorationType(DECORATION_KEY, {}); @@ -187,7 +189,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati if (!input || input.state === State.Inactive) { await this.selectSession(newSession); } - this.updateTitleArea(); + this.updateActions(); })); this._register(this.themeService.onThemeChange(() => { this.refreshReplElements(false); @@ -195,7 +197,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati this.updateInputDecoration(); } })); - this._register(this.onDidChangeVisibility(visible => { + this._register(this.onDidChangeBodyVisibility(visible => { if (!visible) { dispose(this.model); } else { @@ -328,7 +330,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati // Ignore inactive sessions which got cleared - so they are not shown any more sessionsToIgnore.add(session); await this.selectSession(); - this.updateTitleArea(); + this.updateActions(); } } this.replInput.focus(); @@ -345,7 +347,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati this.replInputLineCount = 1; if (shouldRelayout) { // Trigger a layout to shrink a potential multi line input - this.layout(this.dimension); + this.layoutBody(this.dimension.height, this.dimension.width); } } } @@ -366,21 +368,21 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati return removeAnsiEscapeCodes(text); } - layout(dimension: dom.Dimension): void { - this.dimension = dimension; + protected layoutBody(height: number, width: number): void { + this.dimension = new dom.Dimension(width, height); const replInputHeight = Repl.REPL_INPUT_LINE_HEIGHT * this.replInputLineCount; if (this.tree) { const lastElementVisible = this.tree.scrollTop + this.tree.renderHeight >= this.tree.scrollHeight; - const treeHeight = dimension.height - replInputHeight; + const treeHeight = height - replInputHeight; this.tree.getHTMLElement().style.height = `${treeHeight}px`; - this.tree.layout(treeHeight, dimension.width); + this.tree.layout(treeHeight, width); if (lastElementVisible) { revealLastElement(this.tree); } } this.replInputContainer.style.height = `${replInputHeight}px`; - this.replInput.layout({ width: dimension.width - 20, height: replInputHeight }); + this.replInput.layout({ width: width - 20, height: replInputHeight }); } focus(): void { @@ -410,12 +412,12 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati // --- Cached locals @memoize private get selectReplAction(): SelectReplAction { - return this.scopedInstantiationService.createInstance(SelectReplAction, SelectReplAction.ID, SelectReplAction.LABEL); + return this.instantiationService.createInstance(SelectReplAction, SelectReplAction.ID, SelectReplAction.LABEL); } @memoize private get clearReplAction(): ClearReplAction { - return this.scopedInstantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL); + return this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL); } @memoize @@ -436,8 +438,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati // --- Creation - create(parent: HTMLElement): void { - super.create(parent); + protected renderBody(parent: HTMLElement): void { this.container = dom.append(parent, $('.repl')); const treeContainer = dom.append(this.container, $('.repl-tree')); this.createReplInput(this.container); @@ -511,7 +512,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati const lineCount = model ? Math.min(10, model.getLineCount()) : 1; if (lineCount !== this.replInputLineCount) { this.replInputLineCount = lineCount; - this.layout(this.dimension); + this.layoutBody(this.dimension.height, this.dimension.width); } })); // We add the input decoration only when the focus is in the input #61126 @@ -590,7 +591,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati this.replInput.setDecorations(DECORATION_KEY, decorations); } - protected saveState(): void { + saveState(): void { const replHistory = this.history.getHistory(); if (replHistory.length) { this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE); @@ -712,8 +713,6 @@ class SelectReplAction extends Action { } else { await this.replService.selectSession(session); } - - return Promise.resolve(undefined); } } @@ -722,14 +721,14 @@ export class ClearReplAction extends Action { static readonly LABEL = localize('clearRepl', "Clear Console"); constructor(id: string, label: string, - @IPanelService private readonly panelService: IPanelService + @IViewsService private readonly viewsService: IViewsService ) { super(id, label, 'debug-action codicon-clear-all'); } async run(): Promise { - const repl = this.panelService.openPanel(REPL_ID); - await repl.clearRepl(); + const view = await this.viewsService.openView(REPL_VIEW_ID) as Repl; + await view.clearRepl(); aria.status(localize('debugConsoleCleared', "Debug console was cleared")); } } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 55b78e3a7d1..380be6fa530 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -31,7 +31,8 @@ export const WATCH_VIEW_ID = 'workbench.debug.watchExpressionsView'; export const CALLSTACK_VIEW_ID = 'workbench.debug.callStackView'; export const LOADED_SCRIPTS_VIEW_ID = 'workbench.debug.loadedScriptsView'; export const BREAKPOINTS_VIEW_ID = 'workbench.debug.breakPointsView'; -export const REPL_ID = 'workbench.panel.repl'; +export const DEBUG_PANEL_ID = 'workbench.panel.repl'; +export const REPL_VIEW_ID = 'workbench.panel.repl.view'; export const DEBUG_SERVICE_ID = 'debugService'; export const CONTEXT_DEBUG_TYPE = new RawContextKey('debugType', undefined); export const CONTEXT_DEBUG_CONFIGURATION_TYPE = new RawContextKey('debugConfigurationType', undefined); From e7de4f9f8a288406bfba7a97de4636cad3d821c6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jan 2020 16:18:11 +0100 Subject: [PATCH 264/843] remote - ensure errors go into file streams and not unknown serialized data --- .../common/remoteAgentFileSystemChannel.ts | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/vs/platform/remote/common/remoteAgentFileSystemChannel.ts b/src/vs/platform/remote/common/remoteAgentFileSystemChannel.ts index 6abb9086c8d..77e8b56d79c 100644 --- a/src/vs/platform/remote/common/remoteAgentFileSystemChannel.ts +++ b/src/vs/platform/remote/common/remoteAgentFileSystemChannel.ts @@ -15,6 +15,7 @@ import { OperatingSystem } from 'vs/base/common/platform'; import { newWriteableStream, ReadableStreamEvents, ReadableStreamEventPayload } from 'vs/base/common/stream'; import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; export const REMOTE_FILE_SYSTEM_CHANNEL_NAME = 'remotefilesystem'; @@ -32,13 +33,13 @@ export class RemoteFileSystemProvider extends Disposable implements private readonly session: string = generateUuid(); private readonly _onDidChange = this._register(new Emitter()); - readonly onDidChangeFile: Event = this._onDidChange.event; + readonly onDidChangeFile = this._onDidChange.event; - private _onDidWatchErrorOccur: Emitter = this._register(new Emitter()); - readonly onDidErrorOccur: Event = this._onDidWatchErrorOccur.event; + private _onDidWatchErrorOccur = this._register(new Emitter()); + readonly onDidErrorOccur = this._onDidWatchErrorOccur.event; private readonly _onDidChangeCapabilities = this._register(new Emitter()); - readonly onDidChangeCapabilities: Event = this._onDidChangeCapabilities.event; + readonly onDidChangeCapabilities = this._onDidChangeCapabilities.event; private _capabilities!: FileSystemProviderCapabilities; get capabilities(): FileSystemProviderCapabilities { return this._capabilities; } @@ -117,14 +118,29 @@ export class RemoteFileSystemProvider extends Disposable implements // Reading as file stream goes through an event to the remote side const listener = this.channel.listen>('readFileStream', [resource, opts])(dataOrErrorOrEnd => { + + // data if (dataOrErrorOrEnd instanceof VSBuffer) { - - // data: forward into the stream stream.write(dataOrErrorOrEnd.buffer); - } else { + } - // error / end: always end the stream on errors too - stream.end(dataOrErrorOrEnd === 'end' ? undefined : dataOrErrorOrEnd); + // end or error + else { + if (dataOrErrorOrEnd === 'end') { + stream.end(); + } else { + + // Since we receive data through a IPC channel, it is likely + // that the error was not serialized, or only partially. To + // ensure our API use is correct, we convert the data to an + // error here to forward it properly. + let error = dataOrErrorOrEnd; + if (!(error instanceof Error)) { + error = new Error(toErrorMessage(error)); + } + + stream.end(error); + } // Signal to the remote side that we no longer listen listener.dispose(); From 1665beab9e5255e93d0de21e31f0a05d2841a671 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 14 Jan 2020 16:28:28 +0100 Subject: [PATCH 265/843] move TitleMenus from customView to ViewPane, also fixes #88557 --- .../browser/parts/views/customView.ts | 66 +++---------------- .../browser/parts/views/viewPaneContainer.ts | 63 ++++++++++++++++-- .../bulkEdit/browser/bulkEdit.contribution.ts | 49 +++++++++++--- .../contrib/bulkEdit/browser/bulkEditPane.ts | 15 +---- .../contrib/debug/browser/breakpointsView.ts | 4 +- .../contrib/debug/browser/callStackView.ts | 4 +- .../debug/browser/loadedScriptsView.ts | 4 +- .../contrib/debug/browser/startView.ts | 6 +- .../contrib/debug/browser/variablesView.ts | 4 +- .../debug/browser/watchExpressionsView.ts | 4 +- .../extensions/browser/extensionsViews.ts | 2 +- .../electron-browser/extensionsViews.test.ts | 6 +- .../contrib/files/browser/views/emptyView.ts | 4 +- .../files/browser/views/explorerView.ts | 4 +- .../files/browser/views/openEditorsView.ts | 4 +- .../contrib/markers/browser/markersView.ts | 4 +- .../contrib/outline/browser/outlinePane.ts | 2 +- .../contrib/remote/browser/remote.ts | 2 +- .../contrib/remote/browser/tunnelView.ts | 2 +- .../workbench/contrib/scm/browser/mainPane.ts | 4 +- .../contrib/scm/browser/repositoryPane.ts | 2 +- .../contrib/search/browser/searchView.ts | 4 +- 22 files changed, 145 insertions(+), 114 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 6d582d72c39..ceefbf34af6 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -5,13 +5,13 @@ import 'vs/css!./media/views'; import { Event, Emitter } from 'vs/base/common/event'; -import { IDisposable, Disposable, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IAction, IActionViewItem, ActionRunner, Action } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; -import { ContextAwareMenuEntryActionViewItem, createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { ContextAwareMenuEntryActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ViewContainer, ITreeItemLabel, Extensions } from 'vs/workbench/common/views'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; @@ -54,8 +54,9 @@ export class CustomTreeViewPane extends ViewPane { @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, + @IInstantiationService instantiationService: IInstantiationService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); const { treeView } = (Registry.as(Extensions.ViewsRegistry).getView(options.id)); this.treeView = treeView; this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this)); @@ -81,11 +82,11 @@ export class CustomTreeViewPane extends ViewPane { } getActions(): IAction[] { - return [...this.treeView.getPrimaryActions()]; + return [...super.getActions(), ...this.treeView.getPrimaryActions()]; } getSecondaryActions(): IAction[] { - return [...this.treeView.getSecondaryActions()]; + return [...super.getSecondaryActions(), ...this.treeView.getSecondaryActions()]; } getActionViewItem(action: IAction): IActionViewItem | undefined { @@ -101,51 +102,6 @@ export class CustomTreeViewPane extends ViewPane { } } -class TitleMenus extends Disposable { - - private titleActions: IAction[] = []; - private readonly titleActionsDisposable = this._register(new MutableDisposable()); - private titleSecondaryActions: IAction[] = []; - - private _onDidChangeTitle = this._register(new Emitter()); - readonly onDidChangeTitle: Event = this._onDidChangeTitle.event; - - constructor( - id: string, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IMenuService private readonly menuService: IMenuService, - ) { - super(); - - const scopedContextKeyService = this._register(this.contextKeyService.createScoped()); - scopedContextKeyService.createKey('view', id); - - const titleMenu = this._register(this.menuService.createMenu(MenuId.ViewTitle, scopedContextKeyService)); - const updateActions = () => { - this.titleActions = []; - this.titleSecondaryActions = []; - this.titleActionsDisposable.value = createAndFillInActionBarActions(titleMenu, undefined, { primary: this.titleActions, secondary: this.titleSecondaryActions }); - this._onDidChangeTitle.fire(); - }; - - this._register(titleMenu.onDidChange(updateActions)); - updateActions(); - - this._register(toDisposable(() => { - this.titleActions = []; - this.titleSecondaryActions = []; - })); - } - - getTitleActions(): IAction[] { - return this.titleActions; - } - - getTitleSecondaryActions(): IAction[] { - return this.titleSecondaryActions; - } -} - class Root implements ITreeItem { label = { label: 'root' }; handle = '0'; @@ -175,7 +131,6 @@ export class CustomTreeView extends Disposable implements ITreeView { private root: ITreeItem; private elementsToRefresh: ITreeItem[] = []; - private menus: TitleMenus; private readonly _onDidExpandItem: Emitter = this._register(new Emitter()); readonly onDidExpandItem: Event = this._onDidExpandItem.event; @@ -211,8 +166,6 @@ export class CustomTreeView extends Disposable implements ITreeView { ) { super(); this.root = new Root(); - this.menus = this._register(instantiationService.createInstance(TitleMenus, this.id)); - this._register(this.menus.onDidChangeTitle(() => this._onDidChangeActions.fire())); this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); this._register(this.themeService.onThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); this._register(this.configurationService.onDidChangeConfiguration(e => { @@ -309,15 +262,14 @@ export class CustomTreeView extends Disposable implements ITreeView { getPrimaryActions(): IAction[] { if (this.showCollapseAllAction) { - const collapseAllAction = new Action('vs.tree.collapse', localize('collapseAll', "Collapse All"), 'monaco-tree-action codicon-collapse-all', true, () => this.tree ? new CollapseAllAction(this.tree, true).run() : Promise.resolve()); - return [...this.menus.getTitleActions(), collapseAllAction]; + return [new Action('vs.tree.collapse', localize('collapseAll', "Collapse All"), 'monaco-tree-action codicon-collapse-all', true, () => this.tree ? new CollapseAllAction(this.tree, true).run() : Promise.resolve())]; } else { - return this.menus.getTitleActions(); + return []; } } getSecondaryActions(): IAction[] { - return this.menus.getTitleSecondaryActions(); + return []; } setVisibility(isVisible: boolean): void { diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 95095030c48..822718b18f3 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -10,7 +10,7 @@ import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; import { attachStyler, IColorMapping } from 'vs/platform/theme/common/styler'; import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER } from 'vs/workbench/common/theme'; import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener } from 'vs/base/browser/dom'; -import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, combinedDisposable, dispose, toDisposable, Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { firstIndex } from 'vs/base/common/arrays'; import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions'; import { IActionViewItem, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -36,6 +36,8 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IViewPaneContainer } from 'vs/workbench/common/viewPaneContainer'; import { Component } from 'vs/workbench/common/component'; +import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { createAndFillInActionBarActions, ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; export interface IPaneColors extends IColorMapping { dropBackground?: ColorIdentifier; @@ -73,6 +75,8 @@ export abstract class ViewPane extends Pane implements IView { readonly id: string; title: string; + private readonly menuActions: CustomViewMenuActions; + protected actionRunner?: IActionRunner; protected toolbar?: ToolBar; private readonly showActionsAlways: boolean = false; @@ -85,7 +89,8 @@ export abstract class ViewPane extends Pane implements IView { @IKeybindingService protected keybindingService: IKeybindingService, @IContextMenuService protected contextMenuService: IContextMenuService, @IConfigurationService protected readonly configurationService: IConfigurationService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService, + @IInstantiationService protected instantiationService: IInstantiationService, ) { super(options); @@ -94,6 +99,9 @@ export abstract class ViewPane extends Pane implements IView { this.actionRunner = options.actionRunner; this.showActionsAlways = !!options.showActionsAlways; this.focusedViewContextKey = FocusedViewContext.bindTo(contextKeyService); + + this.menuActions = this._register(instantiationService.createInstance(CustomViewMenuActions, this.id)); + this._register(this.menuActions.onDidChangeTitle(() => this.updateActions())); } setVisible(visible: boolean): void { @@ -207,14 +215,17 @@ export abstract class ViewPane extends Pane implements IView { } getActions(): IAction[] { - return []; + return this.menuActions.getPrimaryActions(); } getSecondaryActions(): IAction[] { - return []; + return this.menuActions.getSecondaryActions(); } getActionViewItem(action: IAction): IActionViewItem | undefined { + if (action instanceof MenuItemAction) { + return this.instantiationService.createInstance(ContextAwareMenuEntryActionViewItem, action); + } return undefined; } @@ -759,3 +770,47 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { } +class CustomViewMenuActions extends Disposable { + + private primaryActions: IAction[] = []; + private readonly titleActionsDisposable = this._register(new MutableDisposable()); + private secondaryActions: IAction[] = []; + + private _onDidChangeTitle = this._register(new Emitter()); + readonly onDidChangeTitle: Event = this._onDidChangeTitle.event; + + constructor( + id: string, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IMenuService private readonly menuService: IMenuService, + ) { + super(); + + const scopedContextKeyService = this._register(this.contextKeyService.createScoped()); + scopedContextKeyService.createKey('view', id); + + const titleMenu = this._register(this.menuService.createMenu(MenuId.ViewTitle, scopedContextKeyService)); + const updateActions = () => { + this.primaryActions = []; + this.secondaryActions = []; + this.titleActionsDisposable.value = createAndFillInActionBarActions(titleMenu, undefined, { primary: this.primaryActions, secondary: this.secondaryActions }); + this._onDidChangeTitle.fire(); + }; + + this._register(titleMenu.onDidChange(updateActions)); + updateActions(); + + this._register(toDisposable(() => { + this.primaryActions = []; + this.secondaryActions = []; + })); + } + + getPrimaryActions(): IAction[] { + return this.primaryActions; + } + + getSecondaryActions(): IAction[] { + return this.secondaryActions; + } +} diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index 104ed75e937..ec51b044e9c 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -23,6 +23,8 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { URI } from 'vs/base/common/uri'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; function getBulkEditPane(panelService: IPanelService): BulkEditPane | undefined { let view: ViewPane | undefined; @@ -102,20 +104,51 @@ class BulkEditPreviewContribution { } } -KeybindingsRegistry.registerCommandAndKeybindingRule({ +// CMD: accept +CommandsRegistry.registerCommand('refactorPreview.apply', accessor => { + const panelService = accessor.get(IPanelService); + const view = getBulkEditPane(panelService); + if (view) { + view.accept(); + } +}); +KeybindingsRegistry.registerKeybindingRule({ id: 'refactorPreview.apply', weight: KeybindingWeight.WorkbenchContrib, when: BulkEditPreviewContribution.ctxEnabled, primary: KeyMod.Shift + KeyCode.Enter, - handler(accessor) { - const panelService = accessor.get(IPanelService); - const view = getBulkEditPane(panelService); - if (view) { - view.accept(); - } - } +}); +MenuRegistry.appendMenuItem(MenuId.ViewTitle, { + command: { + id: 'refactorPreview.apply', + title: { value: localize('apply', "Apply Changes"), original: 'Apply Changes' }, + icon: { id: 'codicon/check' }, + precondition: BulkEditPreviewContribution.ctxEnabled + }, + when: ContextKeyExpr.equals('view', BulkEditPane.ID), + group: 'navigation' }); +// CMD: discard +CommandsRegistry.registerCommand('refactorPreview.discard', accessor => { + const panelService = accessor.get(IPanelService); + const view = getBulkEditPane(panelService); + if (view) { + view.discard(); + } +}); +MenuRegistry.appendMenuItem(MenuId.ViewTitle, { + command: { + id: 'refactorPreview.discard', + title: { value: localize('Discard', "Discard Changes"), original: 'Discard Changes' }, + icon: { id: 'codicon/clear-all' }, + precondition: BulkEditPreviewContribution.ctxEnabled + }, + when: ContextKeyExpr.equals('view', BulkEditPane.ID), + group: 'navigation' +}); + +// CMD: toggle KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'refactorPreview.toggleCheckedState', weight: KeybindingWeight.WorkbenchContrib, diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 982e5704712..44f82cbc7c6 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -10,7 +10,6 @@ import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElement import { FuzzyScore } from 'vs/base/common/filters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; -import { Action } from 'vs/base/common/actions'; import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -42,9 +41,6 @@ export class BulkEditPane extends ViewPane { private _tree!: WorkbenchAsyncDataTree; private _message!: HTMLSpanElement; - private readonly _acceptAction = new Action('ok', localize('ok', "Apply Changes"), 'codicon-check', false, async () => this.accept()); - private readonly _discardAction = new Action('discard', localize('discard', "Discard Changes"), 'codicon-clear-all', false, async () => this.discard()); - private readonly _disposables = new DisposableStore(); private readonly _sessionDisposables = new DisposableStore(); @@ -65,7 +61,7 @@ export class BulkEditPane extends ViewPane { ) { super( options, - keybindingService, contextMenuService, configurationService, contextKeyService + keybindingService, contextMenuService, configurationService, contextKeyService, _instaService ); this.element.classList.add('bulk-edit-panel', 'show-file-icons'); @@ -124,10 +120,6 @@ export class BulkEditPane extends ViewPane { this._setState(State.Message); } - getActions() { - return [this._acceptAction, this._discardAction]; - } - protected layoutBody(height: number, width: number): void { this._tree.layout(height, width); } @@ -152,9 +144,6 @@ export class BulkEditPane extends ViewPane { this._currentInput = input; - this._acceptAction.enabled = true; - this._discardAction.enabled = true; - return new Promise(async resolve => { this._currentResolve = resolve; @@ -207,8 +196,6 @@ export class BulkEditPane extends ViewPane { private _done(accept: boolean): void { if (this._currentResolve) { this._currentResolve(accept ? this._currentInput?.asWorkspaceEdit() : undefined); - this._acceptAction.enabled = false; - this._discardAction.enabled = false; this._currentInput = undefined; } this._setState(State.Message); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 3e402cfb207..1c2711791de 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -61,14 +61,14 @@ export class BreakpointsView extends ViewPane { @IContextMenuService contextMenuService: IContextMenuService, @IDebugService private readonly debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IThemeService private readonly themeService: IThemeService, @IEditorService private readonly editorService: IEditorService, @IContextViewService private readonly contextViewService: IContextViewService, @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this.minimumBodySize = this.maximumBodySize = getExpandedBodySize(this.debugService.getModel()); this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange())); diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 7314cb261a9..966695aaaf6 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -91,13 +91,13 @@ export class CallStackView extends ViewPane { @IContextMenuService contextMenuService: IContextMenuService, @IDebugService private readonly debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IEditorService private readonly editorService: IEditorService, @IConfigurationService configurationService: IConfigurationService, @IMenuService menuService: IMenuService, @IContextKeyService readonly contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this.callStackItemType = CONTEXT_CALLSTACK_ITEM_TYPE.bindTo(contextKeyService); this.contributedContextMenu = menuService.createMenu(MenuId.DebugCallStackContext, contextKeyService); diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index 0866f659ed8..1bdb1efa5ba 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -402,7 +402,7 @@ export class LoadedScriptsView extends ViewPane { options: IViewletViewOptions, @IContextMenuService contextMenuService: IContextMenuService, @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @IEditorService private readonly editorService: IEditorService, @IContextKeyService readonly contextKeyService: IContextKeyService, @@ -411,7 +411,7 @@ export class LoadedScriptsView extends ViewPane { @IDebugService private readonly debugService: IDebugService, @ILabelService private readonly labelService: ILabelService ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService); } diff --git a/src/vs/workbench/contrib/debug/browser/startView.ts b/src/vs/workbench/contrib/debug/browser/startView.ts index 6e7aee1172d..d45f2e4700c 100644 --- a/src/vs/workbench/contrib/debug/browser/startView.ts +++ b/src/vs/workbench/contrib/debug/browser/startView.ts @@ -23,6 +23,7 @@ import { equals } from 'vs/base/common/arrays'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; const $ = dom.$; export class StartView extends ViewPane { @@ -47,9 +48,10 @@ export class StartView extends ViewPane { @IDebugService private readonly debugService: IDebugService, @IEditorService private readonly editorService: IEditorService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, - @IFileDialogService private readonly dialogService: IFileDialogService + @IFileDialogService private readonly dialogService: IFileDialogService, + @IInstantiationService instantiationService: IInstantiationService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this._register(editorService.onDidActiveEditorChange(() => this.updateView())); this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(() => this.updateView())); } diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index d94bbb7b945..71b9e9f8d32 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -50,11 +50,11 @@ export class VariablesView extends ViewPane { @IDebugService private readonly debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService, @IConfigurationService configurationService: IConfigurationService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IClipboardService private readonly clipboardService: IClipboardService, @IContextKeyService contextKeyService: IContextKeyService ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); // Use scheduler to prevent unnecessary flashing this.onFocusStackFrameScheduler = new RunOnceScheduler(async () => { diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index 7ee2d0fcaca..e21d0c884a7 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -47,11 +47,11 @@ export class WatchExpressionsView extends ViewPane { @IContextMenuService contextMenuService: IContextMenuService, @IDebugService private readonly debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('watchExpressionsSection', "Watch Expressions Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('watchExpressionsSection', "Watch Expressions Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => { this.needsRefresh = false; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 6047594ffb5..477532889fd 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -110,7 +110,7 @@ export class ExtensionsListView extends ViewPane { @IContextKeyService private readonly contextKeyService: IContextKeyService, @IMenuService private readonly menuService: IMenuService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title, showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title, showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this.server = options.server; } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index df34fc18de0..44ba84328b1 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -27,7 +27,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { TestContextService, TestSharedProcessService } from 'vs/workbench/test/workbenchTestServices'; +import { TestContextService, TestSharedProcessService, TestMenuService } from 'vs/workbench/test/workbenchTestServices'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { URLService } from 'vs/platform/url/node/urlService'; @@ -44,6 +44,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { ILabelService } from 'vs/platform/label/common/label'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { IMenuService } from 'vs/platform/actions/common/actions'; suite('ExtensionsListView Tests', () => { @@ -92,7 +93,8 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event); instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event); instantiationService.stub(IRemoteAgentService, RemoteAgentService); - instantiationService.stub(IContextKeyService, MockContextKeyService); + instantiationService.stub(IContextKeyService, new MockContextKeyService()); + instantiationService.stub(IMenuService, new TestMenuService()); instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService { private _localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', authority: 'vscode-local' }; diff --git a/src/vs/workbench/contrib/files/browser/views/emptyView.ts b/src/vs/workbench/contrib/files/browser/views/emptyView.ts index 65959f41a1e..350b41d643c 100644 --- a/src/vs/workbench/contrib/files/browser/views/emptyView.ts +++ b/src/vs/workbench/contrib/files/browser/views/emptyView.ts @@ -37,7 +37,7 @@ export class EmptyView extends ViewPane { constructor( options: IViewletViewOptions, @IThemeService private readonly themeService: IThemeService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @@ -46,7 +46,7 @@ export class EmptyView extends ViewPane { @ILabelService private labelService: ILabelService, @IContextKeyService contextKeyService: IContextKeyService ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this._register(this.contextService.onDidChangeWorkbenchState(() => this.setLabels())); this._register(this.labelService.onDidChangeFormatters(() => this.setLabels())); } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 08a6a6eba05..1639ca2e113 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -107,7 +107,7 @@ export class ExplorerView extends ViewPane { constructor( options: IViewPaneOptions, @IContextMenuService contextMenuService: IContextMenuService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IProgressService private readonly progressService: IProgressService, @IEditorService private readonly editorService: IEditorService, @@ -125,7 +125,7 @@ export class ExplorerView extends ViewPane { @IClipboardService private clipboardService: IClipboardService, @IFileService private readonly fileService: IFileService ) { - super({ ...(options as IViewPaneOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this.resourceContext = instantiationService.createInstance(ResourceContextKey); this._register(this.resourceContext); diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index f5166ae8ee8..a5c809c541b 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -67,7 +67,7 @@ export class OpenEditorsView extends ViewPane { constructor( options: IViewletViewOptions, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IContextMenuService contextMenuService: IContextMenuService, @IEditorService private readonly editorService: IEditorService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -83,7 +83,7 @@ export class OpenEditorsView extends ViewPane { super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize({ key: 'openEditosrSection', comment: ['Open is an adjective'] }, "Open Editors Section"), - }, keybindingService, contextMenuService, configurationService, contextKeyService); + }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this.structuralRefreshDelay = 0; this.listRefreshScheduler = new RunOnceScheduler(() => { diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 20c6c153a44..03106bdf8b9 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -101,7 +101,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { constructor( options: IViewPaneOptions, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IEditorService private readonly editorService: IEditorService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @@ -113,7 +113,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { @IKeybindingService keybindingService: IKeybindingService, @IStorageService storageService: IStorageService, ) { - super({ ...(options as IViewPaneOptions), id: Constants.MARKERS_VIEW_ID, ariaHeaderLabel: Messages.MARKERS_PANEL_TITLE_PROBLEMS }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), id: Constants.MARKERS_VIEW_ID, ariaHeaderLabel: Messages.MARKERS_PANEL_TITLE_PROBLEMS }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this.panelFoucusContextKey = Constants.MarkerPanelFocusContextKey.bindTo(contextKeyService); this.panelState = new Memento(Constants.MARKERS_PANEL_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE); this.markersViewModel = this._register(instantiationService.createInstance(MarkersViewModel, this.panelState['multiline'])); diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index ed98d3814be..598b4cab644 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -266,7 +266,7 @@ export class OutlinePane extends ViewPane { @IContextKeyService contextKeyService: IContextKeyService, @IContextMenuService contextMenuService: IContextMenuService, ) { - super(options, keybindingService, contextMenuService, _configurationService, contextKeyService); + super(options, keybindingService, contextMenuService, _configurationService, contextKeyService, _instantiationService); this._outlineViewState.restore(this._storageService); this._contextKeyFocused = OutlineViewFocused.bindTo(contextKeyService); this._contextKeyFiltered = OutlineViewFiltered.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 27ce85e8cb3..af5e22dcfef 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -368,7 +368,7 @@ class HelpPanel extends ViewPane { @IRemoteExplorerService protected readonly remoteExplorerService: IRemoteExplorerService, @IWorkbenchEnvironmentService protected readonly workbenchEnvironmentService: IWorkbenchEnvironmentService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); } protected renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index c88132b8b2c..4a16572e0dd 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -415,7 +415,7 @@ export class TunnelPanel extends ViewPane { @IThemeService private readonly themeService: IThemeService, @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this.tunnelTypeContext = TunnelTypeContextKey.bindTo(contextKeyService); this.tunnelCloseableContext = TunnelCloseableContextKey.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/scm/browser/mainPane.ts b/src/vs/workbench/contrib/scm/browser/mainPane.ts index f44e13e5e9c..695e9325011 100644 --- a/src/vs/workbench/contrib/scm/browser/mainPane.ts +++ b/src/vs/workbench/contrib/scm/browser/mainPane.ts @@ -183,12 +183,12 @@ export class MainPane extends ViewPane { @IKeybindingService protected keybindingService: IKeybindingService, @IContextMenuService protected contextMenuService: IContextMenuService, @ISCMService protected scmService: ISCMService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IMenuService private readonly menuService: IMenuService, @IConfigurationService configurationService: IConfigurationService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); } protected renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index 95626a88b2c..012392258b6 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -618,7 +618,7 @@ export class RepositoryPane extends ViewPane { @IMenuService protected menuService: IMenuService, @IStorageService private storageService: IStorageService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this.menus = instantiationService.createInstance(SCMMenus, this.repository.provider); this._register(this.menus); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 1a7005618f5..cd0d8fe620b 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -156,7 +156,7 @@ export class SearchView extends ViewPane { @INotificationService private readonly notificationService: INotificationService, @IDialogService private readonly dialogService: IDialogService, @IContextViewService private readonly contextViewService: IContextViewService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @ISearchWorkbenchService private readonly searchWorkbenchService: ISearchWorkbenchService, @@ -174,7 +174,7 @@ export class SearchView extends ViewPane { @ILabelService private readonly labelService: ILabelService, @IOpenerService private readonly openerService: IOpenerService ) { - super({ ...options, id: VIEW_ID, ariaHeaderLabel: nls.localize('searchView', "Search") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...options, id: VIEW_ID, ariaHeaderLabel: nls.localize('searchView', "Search") }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this.viewletVisible = Constants.SearchViewVisibleKey.bindTo(contextKeyService); this.viewletFocused = Constants.SearchViewFocusedKey.bindTo(contextKeyService); From 49aa355e06db26a8899c7fa6f49bba89187e411d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jan 2020 16:30:21 +0100 Subject: [PATCH 266/843] fix #86843 --- src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index df3a76569fa..d556bdf660c 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -1123,6 +1123,8 @@ export class DirtyDiffModel extends Disposable { this.originalModelDisposables.add(ref.object.textEditorModel.onDidChangeContent(() => this.triggerDiff())); return originalUri; + }).catch(error => { + return null; // possibly invalid reference }); }); From e7459ebe6eaeff26aeb7b2bb5d14a7a6494b24e3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jan 2020 16:30:47 +0100 Subject: [PATCH 267/843] config - no need to check for file exist on error --- .../services/configuration/browser/configuration.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 455ea3af671..ac42737797c 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -9,7 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import * as errors from 'vs/base/common/errors'; import { Disposable, IDisposable, dispose, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; -import { FileChangeType, FileChangesEvent, IFileService, whenProviderRegistered } from 'vs/platform/files/common/files'; +import { FileChangeType, FileChangesEvent, IFileService, whenProviderRegistered, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels'; import { WorkspaceConfigurationModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels'; import { TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, REMOTE_MACHINE_SCOPES, FOLDER_SCOPES, WORKSPACE_SCOPES } from 'vs/workbench/services/configuration/common/configuration'; @@ -127,8 +127,7 @@ class FileServiceBasedConfigurationWithNames extends Disposable { const content = await this.fileService.readFile(resource); return content.value.toString(); } catch (error) { - const exists = await this.fileService.exists(resource); - if (exists) { + if ((error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) { errors.onUnexpectedError(error); } } From b4a79d682a48fd9b5a1353ba08e36ad424c2b925 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 14 Jan 2020 15:09:39 +0100 Subject: [PATCH 268/843] Add getters for computed editor options (Fixes microsoft/monaco-editor#1734) --- build/gulpfile.editor.js | 7 + build/monaco/api.js | 53 +- build/monaco/api.ts | 63 +- build/monaco/monaco.d.ts.recipe | 3 +- src/vs/editor/browser/editorBrowser.ts | 4 +- src/vs/editor/common/config/editorOptions.ts | 73 +- .../common/standalone/standaloneEnums.ts | 726 ++++++++++++------ .../standalone/browser/standaloneEditor.ts | 28 +- src/vs/monaco.d.ts | 401 +++++++++- 9 files changed, 1010 insertions(+), 348 deletions(-) diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index bc11c8b5034..b686d280189 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -353,6 +353,13 @@ gulp.task('editor-distro', ) ); +gulp.task('monacodts', task.define('monacodts', () => { + const result = monacoapi.execute(); + fs.writeFileSync(result.filePath, result.content); + fs.writeFileSync(path.join(root, 'src/vs/editor/common/standalone/standaloneEnums.ts'), result.enums); + return Promise.resolve(true); +})); + //#region monaco type checking function createTscCompileTask(watch) { diff --git a/build/monaco/api.js b/build/monaco/api.js index a429cd66cde..ee9505c0595 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -9,7 +9,7 @@ const ts = require("typescript"); const path = require("path"); const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); -const dtsv = '2'; +const dtsv = '3'; const tsfmt = require('../../tsfmt.json'); const SRC = path.join(__dirname, '../../src'); exports.RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); @@ -148,12 +148,35 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, } }); } + else if (declaration.kind === ts.SyntaxKind.VariableStatement) { + const jsDoc = result.substr(0, declaration.getLeadingTriviaWidth(sourceFile)); + if (jsDoc.indexOf('@monacodtsreplace') >= 0) { + const jsDocLines = jsDoc.split(/\r\n|\r|\n/); + let directives = []; + for (const jsDocLine of jsDocLines) { + const m = jsDocLine.match(/^\s*\* \/([^/]+)\/([^/]+)\/$/); + if (m) { + directives.push([new RegExp(m[1], 'g'), m[2]]); + } + } + // remove the jsdoc + result = result.substr(jsDoc.length); + if (directives.length > 0) { + // apply replace directives + const replacer = createReplacerFromDirectives(directives); + result = replacer(result); + } + } + } result = result.replace(/export default /g, 'export '); result = result.replace(/export declare /g, 'export '); result = result.replace(/declare /g, ''); if (declaration.kind === ts.SyntaxKind.EnumDeclaration) { result = result.replace(/const enum/, 'enum'); - enums.push(result); + enums.push({ + enumName: declaration.name.getText(sourceFile), + text: result + }); } return result; } @@ -277,6 +300,14 @@ function format(text, endl) { return result; } } +function createReplacerFromDirectives(directives) { + return (str) => { + for (let i = 0; i < directives.length; i++) { + str = str.replace(directives[i][0], directives[i][1]); + } + return str; + }; +} function createReplacer(data) { data = data || ''; let rawDirectives = data.split(';'); @@ -292,12 +323,7 @@ function createReplacer(data) { findStr = '\\b' + findStr + '\\b'; directives.push([new RegExp(findStr, 'g'), replaceStr]); }); - return (str) => { - for (let i = 0; i < directives.length; i++) { - str = str.replace(directives[i][0], directives[i][1]); - } - return str; - }; + return createReplacerFromDirectives(directives); } function generateDeclarationFile(recipe, sourceFileGetter) { const endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; @@ -415,6 +441,15 @@ function generateDeclarationFile(recipe, sourceFileGetter) { resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl); resultTxt = format(resultTxt, endl); resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl); + enums.sort((e1, e2) => { + if (e1.enumName < e2.enumName) { + return -1; + } + if (e1.enumName > e2.enumName) { + return 1; + } + return 0; + }); let resultEnums = [ '/*---------------------------------------------------------------------------------------------', ' * Copyright (c) Microsoft Corporation. All rights reserved.', @@ -423,7 +458,7 @@ function generateDeclarationFile(recipe, sourceFileGetter) { '', '// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.', '' - ].concat(enums).join(endl); + ].concat(enums.map(e => e.text)).join(endl); resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl); resultEnums = format(resultEnums, endl); resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl); diff --git a/build/monaco/api.ts b/build/monaco/api.ts index ae058292344..b52b0b63431 100644 --- a/build/monaco/api.ts +++ b/build/monaco/api.ts @@ -9,7 +9,7 @@ import * as path from 'path'; import * as fancyLog from 'fancy-log'; import * as ansiColors from 'ansi-colors'; -const dtsv = '2'; +const dtsv = '3'; const tsfmt = require('../../tsfmt.json'); @@ -138,7 +138,7 @@ function isDefaultExport(declaration: ts.InterfaceDeclaration | ts.ClassDeclarat ); } -function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[], enums: string[]): string { +function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[], enums: IEnumEntry[]): string { let result = getNodeText(sourceFile, declaration); if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { let interfaceDeclaration = declaration; @@ -177,6 +177,25 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati // life.. } }); + } else if (declaration.kind === ts.SyntaxKind.VariableStatement) { + const jsDoc = result.substr(0, declaration.getLeadingTriviaWidth(sourceFile)); + if (jsDoc.indexOf('@monacodtsreplace') >= 0) { + const jsDocLines = jsDoc.split(/\r\n|\r|\n/); + let directives: [RegExp, string][] = []; + for (const jsDocLine of jsDocLines) { + const m = jsDocLine.match(/^\s*\* \/([^/]+)\/([^/]+)\/$/); + if (m) { + directives.push([new RegExp(m[1], 'g'), m[2]]); + } + } + // remove the jsdoc + result = result.substr(jsDoc.length); + if (directives.length > 0) { + // apply replace directives + const replacer = createReplacerFromDirectives(directives); + result = replacer(result); + } + } } result = result.replace(/export default /g, 'export '); result = result.replace(/export declare /g, 'export '); @@ -184,7 +203,10 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati if (declaration.kind === ts.SyntaxKind.EnumDeclaration) { result = result.replace(/const enum/, 'enum'); - enums.push(result); + enums.push({ + enumName: declaration.name.getText(sourceFile), + text: result + }); } return result; @@ -324,6 +346,15 @@ function format(text: string, endl: string): string { } } +function createReplacerFromDirectives(directives: [RegExp, string][]): (str: string) => string { + return (str: string) => { + for (let i = 0; i < directives.length; i++) { + str = str.replace(directives[i][0], directives[i][1]); + } + return str; + }; +} + function createReplacer(data: string): (str: string) => string { data = data || ''; let rawDirectives = data.split(';'); @@ -341,12 +372,7 @@ function createReplacer(data: string): (str: string) => string { directives.push([new RegExp(findStr, 'g'), replaceStr]); }); - return (str: string) => { - for (let i = 0; i < directives.length; i++) { - str = str.replace(directives[i][0], directives[i][1]); - } - return str; - }; + return createReplacerFromDirectives(directives); } interface ITempResult { @@ -355,6 +381,11 @@ interface ITempResult { enums: string; } +interface IEnumEntry { + enumName: string; + text: string; +} + function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGetter): ITempResult | null { const endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; @@ -376,7 +407,7 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet return importName; }; - let enums: string[] = []; + let enums: IEnumEntry[] = []; let version: string | null = null; lines.forEach(line => { @@ -492,6 +523,16 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet resultTxt = format(resultTxt, endl); resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl); + enums.sort((e1, e2) => { + if (e1.enumName < e2.enumName) { + return -1; + } + if (e1.enumName > e2.enumName) { + return 1; + } + return 0; + }); + let resultEnums = [ '/*---------------------------------------------------------------------------------------------', ' * Copyright (c) Microsoft Corporation. All rights reserved.', @@ -500,7 +541,7 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet '', '// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.', '' - ].concat(enums).join(endl); + ].concat(enums.map(e => e.text)).join(endl); resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl); resultEnums = format(resultEnums, endl); resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl); diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index f16ed378bd5..fdcdf533406 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -62,6 +62,7 @@ export interface ICommandHandler { #includeAll(vs/editor/common/editorCommon;editorOptions.=>): IScrollEvent #includeAll(vs/editor/common/model/textModelEvents): #includeAll(vs/editor/common/controller/cursorEvents): +#include(vs/platform/accessibility/common/accessibility): AccessibilitySupport #includeAll(vs/editor/common/config/editorOptions): #includeAll(vs/editor/browser/editorBrowser;editorCommon.=>;editorOptions.=>): #include(vs/editor/common/config/fontInfo): FontInfo, BareFontInfo @@ -87,4 +88,4 @@ declare namespace monaco.worker { } -//dtsv=2 +//dtsv=3 diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index cbbe9d17014..bf098e0699f 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -540,12 +540,12 @@ export interface ICodeEditor extends editorCommon.IEditor { setModel(model: ITextModel | null): void; /** - * @internal + * Gets all the editor computed options. */ getOptions(): IComputedEditorOptions; /** - * @internal + * Gets a specific editor option. */ getOption(id: T): FindComputedEditorOptionValueById; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 100e0a7fdb8..6304bfb3754 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -33,7 +33,6 @@ export type EditorAutoClosingOvertypeStrategy = 'always' | 'auto' | 'never'; /** * Configuration options for auto indentation in the editor - * @internal */ export const enum EditorAutoIndentStrategy { None = 0, @@ -636,7 +635,7 @@ export class ValidatedEditorOptions { } /** - * @internal + * All computed editor options. */ export interface IComputedEditorOptions { get(id: T): FindComputedEditorOptionValueById; @@ -660,9 +659,6 @@ export interface IEnvironmentalOptions { readonly accessibilitySupport: AccessibilitySupport; } -/** - * @internal - */ export interface IEditorOption { readonly id: K1; readonly name: string; @@ -1000,7 +996,6 @@ class EditorAccessibilitySupport extends BaseEditorOption>; class EditorFind extends BaseEditorOption { @@ -1364,9 +1355,6 @@ export interface IGotoLocationOptions { alternativeReferenceCommand?: string; } -/** - * @internal - */ export type GoToLocationOptions = Readonly>; class EditorGoToLocation extends BaseEditorOption { @@ -1496,9 +1484,6 @@ export interface IEditorHoverOptions { sticky?: boolean; } -/** - * @internal - */ export type EditorHoverOptions = Readonly>; class EditorHover extends BaseEditorOption { @@ -1924,9 +1909,6 @@ export interface IEditorLightbulbOptions { enabled?: boolean; } -/** - * @internal - */ export type EditorLightbulbOptions = Readonly>; class EditorLightbulb extends BaseEditorOption { @@ -2018,9 +2000,6 @@ export interface IEditorMinimapOptions { scale?: number; } -/** - * @internal - */ export type EditorMinimapOptions = Readonly>; class EditorMinimap extends BaseEditorOption { @@ -2122,9 +2101,6 @@ export interface IEditorParameterHintOptions { cycle?: boolean; } -/** - * @internal - */ export type InternalParameterHintOptions = Readonly>; class EditorParameterHints extends BaseEditorOption { @@ -2191,9 +2167,6 @@ export interface IQuickSuggestionsOptions { strings: boolean; } -/** - * @internal - */ export type ValidQuickSuggestionsOptions = boolean | Readonly>; class EditorQuickSuggestions extends BaseEditorOption { @@ -2270,9 +2243,6 @@ class EditorQuickSuggestions extends BaseEditorOption string); -/** - * @internal - */ export const enum RenderLineNumbersType { Off = 0, On = 1, @@ -2281,9 +2251,6 @@ export const enum RenderLineNumbersType { Custom = 4 } -/** - * @internal - */ export interface InternalEditorRenderLineNumbersOptions { readonly renderType: RenderLineNumbersType; readonly renderFn: ((lineNumber: number) => string) | null; @@ -2439,9 +2406,6 @@ export interface IEditorScrollbarOptions { horizontalSliderSize?: number; } -/** - * @internal - */ export interface InternalEditorScrollbarOptions { readonly arrowSize: number; readonly vertical: ScrollbarVisibility; @@ -2656,9 +2620,6 @@ export interface ISuggestOptions { showSnippets?: boolean; } -/** - * @internal - */ export type InternalSuggestOptions = Readonly>; class EditorSuggest extends BaseEditorOption { @@ -2952,7 +2913,6 @@ class EditorTabFocusMode extends ComputedEditorOption(option: IEditorOption): IEd return option; } -/** - * @internal - */ export const enum EditorOption { acceptSuggestionOnCommitCharacter, acceptSuggestionOnEnter, @@ -3224,7 +3178,18 @@ export const enum EditorOption { } /** - * @internal + * WORKAROUND: TS emits "any" for complex editor options values (anything except string, bool, enum, etc. ends up being "any") + * @monacodtsreplace + * /accessibilitySupport, any/accessibilitySupport, AccessibilitySupport/ + * /find, any/find, EditorFindOptions/ + * /fontInfo, any/fontInfo, FontInfo/ + * /gotoLocation, any/gotoLocation, GoToLocationOptions/ + * /hover, any/hover, EditorHoverOptions/ + * /lightbulb, any/lightbulb, EditorLightbulbOptions/ + * /minimap, any/minimap, EditorMinimapOptions/ + * /parameterHints, any/parameterHints, InternalParameterHintOptions/ + * /quickSuggestions, any/quickSuggestions, ValidQuickSuggestionsOptions/ + * /suggest, any/suggest, InternalSuggestOptions/ */ export const EditorOptions = { acceptSuggestionOnCommitCharacter: register(new EditorBooleanOption( @@ -3800,19 +3765,7 @@ export const EditorOptions = { wrappingInfo: register(new EditorWrappingInfoComputer()) }; -/** - * @internal - */ type EditorOptionsType = typeof EditorOptions; -/** - * @internal - */ type FindEditorOptionsKeyById = { [K in keyof EditorOptionsType]: EditorOptionsType[K]['id'] extends T ? K : never }[keyof EditorOptionsType]; -/** - * @internal - */ type ComputedEditorOptionValue> = T extends IEditorOption ? R : never; -/** - * @internal - */ export type FindComputedEditorOptionValueById = NonNullable]>>; diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 3340dd5c045..f23767462c3 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -6,16 +6,326 @@ // THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. -export enum MarkerTag { - Unnecessary = 1, - Deprecated = 2 +export enum AccessibilitySupport { + /** + * This should be the browser case where it is not known if a screen reader is attached or no. + */ + Unknown = 0, + Disabled = 1, + Enabled = 2 } -export enum MarkerSeverity { - Hint = 1, - Info = 2, - Warning = 4, - Error = 8 +export enum CompletionItemInsertTextRule { + /** + * Adjust whitespace/indentation of multiline insert texts to + * match the current line indentation. + */ + KeepWhitespace = 1, + /** + * `insertText` is a snippet. + */ + InsertAsSnippet = 4 +} + +export enum CompletionItemKind { + Method = 0, + Function = 1, + Constructor = 2, + Field = 3, + Variable = 4, + Class = 5, + Struct = 6, + Interface = 7, + Module = 8, + Property = 9, + Event = 10, + Operator = 11, + Unit = 12, + Value = 13, + Constant = 14, + Enum = 15, + EnumMember = 16, + Keyword = 17, + Text = 18, + Color = 19, + File = 20, + Reference = 21, + Customcolor = 22, + Folder = 23, + TypeParameter = 24, + Snippet = 25 +} + +export enum CompletionItemTag { + Deprecated = 1 +} + +/** + * How a suggest provider was triggered. + */ +export enum CompletionTriggerKind { + Invoke = 0, + TriggerCharacter = 1, + TriggerForIncompleteCompletions = 2 +} + +/** + * A positioning preference for rendering content widgets. + */ +export enum ContentWidgetPositionPreference { + /** + * Place the content widget exactly at a position + */ + EXACT = 0, + /** + * Place the content widget above a position + */ + ABOVE = 1, + /** + * Place the content widget below a position + */ + BELOW = 2 +} + +/** + * Describes the reason the cursor has changed its position. + */ +export enum CursorChangeReason { + /** + * Unknown or not set. + */ + NotSet = 0, + /** + * A `model.setValue()` was called. + */ + ContentFlush = 1, + /** + * The `model` has been changed outside of this cursor and the cursor recovers its position from associated markers. + */ + RecoverFromMarkers = 2, + /** + * There was an explicit user gesture. + */ + Explicit = 3, + /** + * There was a Paste. + */ + Paste = 4, + /** + * There was an Undo. + */ + Undo = 5, + /** + * There was a Redo. + */ + Redo = 6 +} + +/** + * The default end of line to use when instantiating models. + */ +export enum DefaultEndOfLine { + /** + * Use line feed (\n) as the end of line character. + */ + LF = 1, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 2 +} + +/** + * A document highlight kind. + */ +export enum DocumentHighlightKind { + /** + * A textual occurrence. + */ + Text = 0, + /** + * Read-access of a symbol, like reading a variable. + */ + Read = 1, + /** + * Write-access of a symbol, like writing to a variable. + */ + Write = 2 +} + +/** + * Configuration options for auto indentation in the editor + */ +export enum EditorAutoIndentStrategy { + None = 0, + Keep = 1, + Brackets = 2, + Advanced = 3, + Full = 4 +} + +export enum EditorOption { + acceptSuggestionOnCommitCharacter = 0, + acceptSuggestionOnEnter = 1, + accessibilitySupport = 2, + accessibilityPageSize = 3, + ariaLabel = 4, + autoClosingBrackets = 5, + autoClosingOvertype = 6, + autoClosingQuotes = 7, + autoIndent = 8, + automaticLayout = 9, + autoSurround = 10, + codeLens = 11, + colorDecorators = 12, + contextmenu = 13, + copyWithSyntaxHighlighting = 14, + cursorBlinking = 15, + cursorSmoothCaretAnimation = 16, + cursorStyle = 17, + cursorSurroundingLines = 18, + cursorSurroundingLinesStyle = 19, + cursorWidth = 20, + disableLayerHinting = 21, + disableMonospaceOptimizations = 22, + dragAndDrop = 23, + emptySelectionClipboard = 24, + extraEditorClassName = 25, + fastScrollSensitivity = 26, + find = 27, + fixedOverflowWidgets = 28, + folding = 29, + foldingStrategy = 30, + fontFamily = 31, + fontInfo = 32, + fontLigatures = 33, + fontSize = 34, + fontWeight = 35, + formatOnPaste = 36, + formatOnType = 37, + glyphMargin = 38, + gotoLocation = 39, + hideCursorInOverviewRuler = 40, + highlightActiveIndentGuide = 41, + hover = 42, + inDiffEditor = 43, + letterSpacing = 44, + lightbulb = 45, + lineDecorationsWidth = 46, + lineHeight = 47, + lineNumbers = 48, + lineNumbersMinChars = 49, + links = 50, + matchBrackets = 51, + minimap = 52, + mouseStyle = 53, + mouseWheelScrollSensitivity = 54, + mouseWheelZoom = 55, + multiCursorMergeOverlapping = 56, + multiCursorModifier = 57, + multiCursorPaste = 58, + occurrencesHighlight = 59, + overviewRulerBorder = 60, + overviewRulerLanes = 61, + parameterHints = 62, + quickSuggestions = 63, + quickSuggestionsDelay = 64, + readOnly = 65, + renderControlCharacters = 66, + renderIndentGuides = 67, + renderFinalNewline = 68, + renderLineHighlight = 69, + renderWhitespace = 70, + revealHorizontalRightPadding = 71, + roundedSelection = 72, + rulers = 73, + scrollbar = 74, + scrollBeyondLastColumn = 75, + scrollBeyondLastLine = 76, + selectionClipboard = 77, + selectionHighlight = 78, + selectOnLineNumbers = 79, + showFoldingControls = 80, + showUnused = 81, + snippetSuggestions = 82, + smoothScrolling = 83, + stopRenderingLineAfter = 84, + suggest = 85, + suggestFontSize = 86, + suggestLineHeight = 87, + suggestOnTriggerCharacters = 88, + suggestSelection = 89, + tabCompletion = 90, + useTabStops = 91, + wordSeparators = 92, + wordWrap = 93, + wordWrapBreakAfterCharacters = 94, + wordWrapBreakBeforeCharacters = 95, + wordWrapBreakObtrusiveCharacters = 96, + wordWrapColumn = 97, + wordWrapMinified = 98, + wrappingIndent = 99, + editorClassName = 100, + pixelRatio = 101, + tabFocusMode = 102, + layoutInfo = 103, + wrappingInfo = 104 +} + +/** + * End of line character preference. + */ +export enum EndOfLinePreference { + /** + * Use the end of line character identified in the text buffer. + */ + TextDefined = 0, + /** + * Use line feed (\n) as the end of line character. + */ + LF = 1, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 2 +} + +/** + * End of line character preference. + */ +export enum EndOfLineSequence { + /** + * Use line feed (\n) as the end of line character. + */ + LF = 0, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 1 +} + +/** + * Describes what to do with the indentation when pressing Enter. + */ +export enum IndentAction { + /** + * Insert new line and copy the previous line's indentation. + */ + None = 0, + /** + * Insert new line and indent once (relative to the previous line's indentation). + */ + Indent = 1, + /** + * Insert two new lines: + * - the first one indented which will hold the cursor + * - the second one at the same indentation level + */ + IndentOutdent = 2, + /** + * Insert new line and outdent once (relative to the previous line's indentation). + */ + Outdent = 3 } /** @@ -199,34 +509,16 @@ export enum KeyCode { MAX_VALUE = 112 } -/** - * The direction of a selection. - */ -export enum SelectionDirection { - /** - * The selection starts above where it ends. - */ - LTR = 0, - /** - * The selection starts below where it ends. - */ - RTL = 1 +export enum MarkerSeverity { + Hint = 1, + Info = 2, + Warning = 4, + Error = 8 } -export enum ScrollbarVisibility { - Auto = 1, - Hidden = 2, - Visible = 3 -} - -/** - * Vertical Lane in the overview ruler of the editor. - */ -export enum OverviewRulerLane { - Left = 1, - Center = 2, - Right = 4, - Full = 7 +export enum MarkerTag { + Unnecessary = 1, + Deprecated = 2 } /** @@ -237,144 +529,6 @@ export enum MinimapPosition { Gutter = 2 } -/** - * End of line character preference. - */ -export enum EndOfLinePreference { - /** - * Use the end of line character identified in the text buffer. - */ - TextDefined = 0, - /** - * Use line feed (\n) as the end of line character. - */ - LF = 1, - /** - * Use carriage return and line feed (\r\n) as the end of line character. - */ - CRLF = 2 -} - -/** - * The default end of line to use when instantiating models. - */ -export enum DefaultEndOfLine { - /** - * Use line feed (\n) as the end of line character. - */ - LF = 1, - /** - * Use carriage return and line feed (\r\n) as the end of line character. - */ - CRLF = 2 -} - -/** - * End of line character preference. - */ -export enum EndOfLineSequence { - /** - * Use line feed (\n) as the end of line character. - */ - LF = 0, - /** - * Use carriage return and line feed (\r\n) as the end of line character. - */ - CRLF = 1 -} - -/** - * Describes the behavior of decorations when typing/editing near their edges. - * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` - */ -export enum TrackedRangeStickiness { - AlwaysGrowsWhenTypingAtEdges = 0, - NeverGrowsWhenTypingAtEdges = 1, - GrowsOnlyWhenTypingBefore = 2, - GrowsOnlyWhenTypingAfter = 3 -} - -export enum ScrollType { - Smooth = 0, - Immediate = 1 -} - -/** - * Describes the reason the cursor has changed its position. - */ -export enum CursorChangeReason { - /** - * Unknown or not set. - */ - NotSet = 0, - /** - * A `model.setValue()` was called. - */ - ContentFlush = 1, - /** - * The `model` has been changed outside of this cursor and the cursor recovers its position from associated markers. - */ - RecoverFromMarkers = 2, - /** - * There was an explicit user gesture. - */ - Explicit = 3, - /** - * There was a Paste. - */ - Paste = 4, - /** - * There was an Undo. - */ - Undo = 5, - /** - * There was a Redo. - */ - Redo = 6 -} - -export enum RenderMinimap { - None = 0, - Text = 1, - Blocks = 2 -} - -/** - * A positioning preference for rendering content widgets. - */ -export enum ContentWidgetPositionPreference { - /** - * Place the content widget exactly at a position - */ - EXACT = 0, - /** - * Place the content widget above a position - */ - ABOVE = 1, - /** - * Place the content widget below a position - */ - BELOW = 2 -} - -/** - * A positioning preference for rendering overlay widgets. - */ -export enum OverlayWidgetPositionPreference { - /** - * Position the overlay widget in the top right corner - */ - TOP_RIGHT_CORNER = 0, - /** - * Position the overlay widget in the bottom right corner - */ - BOTTOM_RIGHT_CORNER = 1, - /** - * Position the overlay widget in the top center - */ - TOP_CENTER = 2 -} - /** * Type of hit element with the mouse in the editor. */ @@ -438,81 +592,70 @@ export enum MouseTargetType { } /** - * Describes what to do with the indentation when pressing Enter. + * A positioning preference for rendering overlay widgets. */ -export enum IndentAction { +export enum OverlayWidgetPositionPreference { /** - * Insert new line and copy the previous line's indentation. + * Position the overlay widget in the top right corner */ - None = 0, + TOP_RIGHT_CORNER = 0, /** - * Insert new line and indent once (relative to the previous line's indentation). + * Position the overlay widget in the bottom right corner */ - Indent = 1, + BOTTOM_RIGHT_CORNER = 1, /** - * Insert two new lines: - * - the first one indented which will hold the cursor - * - the second one at the same indentation level + * Position the overlay widget in the top center */ - IndentOutdent = 2, - /** - * Insert new line and outdent once (relative to the previous line's indentation). - */ - Outdent = 3 -} - -export enum CompletionItemKind { - Method = 0, - Function = 1, - Constructor = 2, - Field = 3, - Variable = 4, - Class = 5, - Struct = 6, - Interface = 7, - Module = 8, - Property = 9, - Event = 10, - Operator = 11, - Unit = 12, - Value = 13, - Constant = 14, - Enum = 15, - EnumMember = 16, - Keyword = 17, - Text = 18, - Color = 19, - File = 20, - Reference = 21, - Customcolor = 22, - Folder = 23, - TypeParameter = 24, - Snippet = 25 -} - -export enum CompletionItemTag { - Deprecated = 1 -} - -export enum CompletionItemInsertTextRule { - /** - * Adjust whitespace/indentation of multiline insert texts to - * match the current line indentation. - */ - KeepWhitespace = 1, - /** - * `insertText` is a snippet. - */ - InsertAsSnippet = 4 + TOP_CENTER = 2 } /** - * How a suggest provider was triggered. + * Vertical Lane in the overview ruler of the editor. */ -export enum CompletionTriggerKind { - Invoke = 0, - TriggerCharacter = 1, - TriggerForIncompleteCompletions = 2 +export enum OverviewRulerLane { + Left = 1, + Center = 2, + Right = 4, + Full = 7 +} + +export enum RenderLineNumbersType { + Off = 0, + On = 1, + Relative = 2, + Interval = 3, + Custom = 4 +} + +export enum RenderMinimap { + None = 0, + Text = 1, + Blocks = 2 +} + +export enum ScrollType { + Smooth = 0, + Immediate = 1 +} + +export enum ScrollbarVisibility { + Auto = 1, + Hidden = 2, + Visible = 3 +} + +/** + * The direction of a selection. + */ +export enum SelectionDirection { + /** + * The selection starts above where it ends. + */ + LTR = 0, + /** + * The selection starts below where it ends. + */ + RTL = 1 } export enum SignatureHelpTriggerKind { @@ -521,24 +664,6 @@ export enum SignatureHelpTriggerKind { ContentChange = 3 } -/** - * A document highlight kind. - */ -export enum DocumentHighlightKind { - /** - * A textual occurrence. - */ - Text = 0, - /** - * Read-access of a symbol, like reading a variable. - */ - Read = 1, - /** - * Write-access of a symbol, like writing to a variable. - */ - Write = 2 -} - /** * A symbol kind. */ @@ -573,4 +698,97 @@ export enum SymbolKind { export enum SymbolTag { Deprecated = 1 +} + +/** + * The kind of animation in which the editor's cursor should be rendered. + */ +export enum TextEditorCursorBlinkingStyle { + /** + * Hidden + */ + Hidden = 0, + /** + * Blinking + */ + Blink = 1, + /** + * Blinking with smooth fading + */ + Smooth = 2, + /** + * Blinking with prolonged filled state and smooth fading + */ + Phase = 3, + /** + * Expand collapse animation on the y axis + */ + Expand = 4, + /** + * No-Blinking + */ + Solid = 5 +} + +/** + * The style in which the editor's cursor should be rendered. + */ +export enum TextEditorCursorStyle { + /** + * As a vertical line (sitting between two characters). + */ + Line = 1, + /** + * As a block (sitting on top of a character). + */ + Block = 2, + /** + * As a horizontal line (sitting under a character). + */ + Underline = 3, + /** + * As a thin vertical line (sitting between two characters). + */ + LineThin = 4, + /** + * As an outlined block (sitting on top of a character). + */ + BlockOutline = 5, + /** + * As a thin horizontal line (sitting under a character). + */ + UnderlineThin = 6 +} + +/** + * Describes the behavior of decorations when typing/editing near their edges. + * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` + */ +export enum TrackedRangeStickiness { + AlwaysGrowsWhenTypingAtEdges = 0, + NeverGrowsWhenTypingAtEdges = 1, + GrowsOnlyWhenTypingBefore = 2, + GrowsOnlyWhenTypingAfter = 3 +} + +/** + * Describes how to indent wrapped lines. + */ +export enum WrappingIndent { + /** + * No indentation => wrapped lines begin at column 1. + */ + None = 0, + /** + * Same => wrapped lines get the same indentation as the parent. + */ + Same = 1, + /** + * Indent => wrapped lines get +1 indentation toward the parent. + */ + Indent = 2, + /** + * DeepIndent => wrapped lines get +2 indentation toward the parent. + */ + DeepIndent = 3 } \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 14e54cbb0bb..65285eb9a2d 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -10,7 +10,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { OpenerService } from 'vs/editor/browser/services/openerService'; import { DiffNavigator, IDiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; -import { ConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { EditorOptions, ConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; import { Token } from 'vs/editor/common/core/token'; import * as editorCommon from 'vs/editor/common/editorCommon'; @@ -346,19 +346,26 @@ export function createMonacoEditorAPI(): typeof monaco.editor { remeasureFonts: remeasureFonts, // enums - ScrollbarVisibility: standaloneEnums.ScrollbarVisibility, - OverviewRulerLane: standaloneEnums.OverviewRulerLane, - MinimapPosition: standaloneEnums.MinimapPosition, - EndOfLinePreference: standaloneEnums.EndOfLinePreference, - DefaultEndOfLine: standaloneEnums.DefaultEndOfLine, - EndOfLineSequence: standaloneEnums.EndOfLineSequence, - TrackedRangeStickiness: standaloneEnums.TrackedRangeStickiness, - CursorChangeReason: standaloneEnums.CursorChangeReason, - MouseTargetType: standaloneEnums.MouseTargetType, + AccessibilitySupport: standaloneEnums.AccessibilitySupport, ContentWidgetPositionPreference: standaloneEnums.ContentWidgetPositionPreference, + CursorChangeReason: standaloneEnums.CursorChangeReason, + DefaultEndOfLine: standaloneEnums.DefaultEndOfLine, + EditorAutoIndentStrategy: standaloneEnums.EditorAutoIndentStrategy, + EditorOption: standaloneEnums.EditorOption, + EndOfLinePreference: standaloneEnums.EndOfLinePreference, + EndOfLineSequence: standaloneEnums.EndOfLineSequence, + MinimapPosition: standaloneEnums.MinimapPosition, + MouseTargetType: standaloneEnums.MouseTargetType, OverlayWidgetPositionPreference: standaloneEnums.OverlayWidgetPositionPreference, + OverviewRulerLane: standaloneEnums.OverviewRulerLane, + RenderLineNumbersType: standaloneEnums.RenderLineNumbersType, RenderMinimap: standaloneEnums.RenderMinimap, + ScrollbarVisibility: standaloneEnums.ScrollbarVisibility, ScrollType: standaloneEnums.ScrollType, + TextEditorCursorBlinkingStyle: standaloneEnums.TextEditorCursorBlinkingStyle, + TextEditorCursorStyle: standaloneEnums.TextEditorCursorStyle, + TrackedRangeStickiness: standaloneEnums.TrackedRangeStickiness, + WrappingIndent: standaloneEnums.WrappingIndent, // classes ConfigurationChangedEvent: ConfigurationChangedEvent, @@ -369,6 +376,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor { // vars EditorType: editorCommon.EditorType, + EditorOptions: EditorOptions }; } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 1ff3e1c0cd5..14651914b4f 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2473,6 +2473,15 @@ declare namespace monaco.editor { readonly reason: CursorChangeReason; } + export enum AccessibilitySupport { + /** + * This should be the browser case where it is not known if a screen reader is attached or no. + */ + Unknown = 0, + Disabled = 1, + Enabled = 2 + } + /** * Configuration options for auto closing quotes and brackets */ @@ -2488,6 +2497,17 @@ declare namespace monaco.editor { */ export type EditorAutoClosingOvertypeStrategy = 'always' | 'auto' | 'never'; + /** + * Configuration options for auto indentation in the editor + */ + export enum EditorAutoIndentStrategy { + None = 0, + Keep = 1, + Brackets = 2, + Advanced = 3, + Full = 4 + } + /** * Configuration options for the editor. */ @@ -3043,6 +3063,79 @@ declare namespace monaco.editor { export class ConfigurationChangedEvent { } + /** + * All computed editor options. + */ + export interface IComputedEditorOptions { + get(id: T): FindComputedEditorOptionValueById; + } + + export interface IEditorOption { + readonly id: K1; + readonly name: string; + defaultValue: V; + } + + /** + * The kind of animation in which the editor's cursor should be rendered. + */ + export enum TextEditorCursorBlinkingStyle { + /** + * Hidden + */ + Hidden = 0, + /** + * Blinking + */ + Blink = 1, + /** + * Blinking with smooth fading + */ + Smooth = 2, + /** + * Blinking with prolonged filled state and smooth fading + */ + Phase = 3, + /** + * Expand collapse animation on the y axis + */ + Expand = 4, + /** + * No-Blinking + */ + Solid = 5 + } + + /** + * The style in which the editor's cursor should be rendered. + */ + export enum TextEditorCursorStyle { + /** + * As a vertical line (sitting between two characters). + */ + Line = 1, + /** + * As a block (sitting on top of a character). + */ + Block = 2, + /** + * As a horizontal line (sitting under a character). + */ + Underline = 3, + /** + * As a thin vertical line (sitting between two characters). + */ + LineThin = 4, + /** + * As an outlined block (sitting on top of a character). + */ + BlockOutline = 5, + /** + * As a thin horizontal line (sitting under a character). + */ + UnderlineThin = 6 + } + /** * Configuration options for editor find widget */ @@ -3058,6 +3151,8 @@ declare namespace monaco.editor { addExtraSpaceOnTop?: boolean; } + export type EditorFindOptions = Readonly>; + export type GoToLocationValues = 'peek' | 'gotoAndPeek' | 'goto'; /** @@ -3077,6 +3172,8 @@ declare namespace monaco.editor { alternativeReferenceCommand?: string; } + export type GoToLocationOptions = Readonly>; + /** * Configuration options for editor hover */ @@ -3098,6 +3195,8 @@ declare namespace monaco.editor { sticky?: boolean; } + export type EditorHoverOptions = Readonly>; + /** * Configuration options for semantic highlighting */ @@ -3238,6 +3337,8 @@ declare namespace monaco.editor { enabled?: boolean; } + export type EditorLightbulbOptions = Readonly>; + /** * Configuration options for editor minimap */ @@ -3273,6 +3374,8 @@ declare namespace monaco.editor { scale?: number; } + export type EditorMinimapOptions = Readonly>; + /** * Configuration options for parameter hints */ @@ -3289,6 +3392,8 @@ declare namespace monaco.editor { cycle?: boolean; } + export type InternalParameterHintOptions = Readonly>; + /** * Configuration options for quick suggestions */ @@ -3298,8 +3403,23 @@ declare namespace monaco.editor { strings: boolean; } + export type ValidQuickSuggestionsOptions = boolean | Readonly>; + export type LineNumbersType = 'on' | 'off' | 'relative' | 'interval' | ((lineNumber: number) => string); + export enum RenderLineNumbersType { + Off = 0, + On = 1, + Relative = 2, + Interval = 3, + Custom = 4 + } + + export interface InternalEditorRenderLineNumbersOptions { + readonly renderType: RenderLineNumbersType; + readonly renderFn: ((lineNumber: number) => string) | null; + } + /** * Configuration options for editor scrollbars */ @@ -3366,6 +3486,21 @@ declare namespace monaco.editor { horizontalSliderSize?: number; } + export interface InternalEditorScrollbarOptions { + readonly arrowSize: number; + readonly vertical: ScrollbarVisibility; + readonly horizontal: ScrollbarVisibility; + readonly useShadows: boolean; + readonly verticalHasArrows: boolean; + readonly horizontalHasArrows: boolean; + readonly handleMouseWheel: boolean; + readonly alwaysConsumeMouseWheel: boolean; + readonly horizontalScrollbarSize: number; + readonly horizontalSliderSize: number; + readonly verticalScrollbarSize: number; + readonly verticalSliderSize: number; + } + /** * Configuration options for editor suggest widget */ @@ -3504,6 +3639,262 @@ declare namespace monaco.editor { showSnippets?: boolean; } + export type InternalSuggestOptions = Readonly>; + + /** + * Describes how to indent wrapped lines. + */ + export enum WrappingIndent { + /** + * No indentation => wrapped lines begin at column 1. + */ + None = 0, + /** + * Same => wrapped lines get the same indentation as the parent. + */ + Same = 1, + /** + * Indent => wrapped lines get +1 indentation toward the parent. + */ + Indent = 2, + /** + * DeepIndent => wrapped lines get +2 indentation toward the parent. + */ + DeepIndent = 3 + } + + export interface EditorWrappingInfo { + readonly isDominatedByLongLines: boolean; + readonly isWordWrapMinified: boolean; + readonly isViewportWrapping: boolean; + readonly wrappingColumn: number; + } + + export enum EditorOption { + acceptSuggestionOnCommitCharacter = 0, + acceptSuggestionOnEnter = 1, + accessibilitySupport = 2, + accessibilityPageSize = 3, + ariaLabel = 4, + autoClosingBrackets = 5, + autoClosingOvertype = 6, + autoClosingQuotes = 7, + autoIndent = 8, + automaticLayout = 9, + autoSurround = 10, + codeLens = 11, + colorDecorators = 12, + contextmenu = 13, + copyWithSyntaxHighlighting = 14, + cursorBlinking = 15, + cursorSmoothCaretAnimation = 16, + cursorStyle = 17, + cursorSurroundingLines = 18, + cursorSurroundingLinesStyle = 19, + cursorWidth = 20, + disableLayerHinting = 21, + disableMonospaceOptimizations = 22, + dragAndDrop = 23, + emptySelectionClipboard = 24, + extraEditorClassName = 25, + fastScrollSensitivity = 26, + find = 27, + fixedOverflowWidgets = 28, + folding = 29, + foldingStrategy = 30, + fontFamily = 31, + fontInfo = 32, + fontLigatures = 33, + fontSize = 34, + fontWeight = 35, + formatOnPaste = 36, + formatOnType = 37, + glyphMargin = 38, + gotoLocation = 39, + hideCursorInOverviewRuler = 40, + highlightActiveIndentGuide = 41, + hover = 42, + inDiffEditor = 43, + letterSpacing = 44, + lightbulb = 45, + lineDecorationsWidth = 46, + lineHeight = 47, + lineNumbers = 48, + lineNumbersMinChars = 49, + links = 50, + matchBrackets = 51, + minimap = 52, + mouseStyle = 53, + mouseWheelScrollSensitivity = 54, + mouseWheelZoom = 55, + multiCursorMergeOverlapping = 56, + multiCursorModifier = 57, + multiCursorPaste = 58, + occurrencesHighlight = 59, + overviewRulerBorder = 60, + overviewRulerLanes = 61, + parameterHints = 62, + quickSuggestions = 63, + quickSuggestionsDelay = 64, + readOnly = 65, + renderControlCharacters = 66, + renderIndentGuides = 67, + renderFinalNewline = 68, + renderLineHighlight = 69, + renderWhitespace = 70, + revealHorizontalRightPadding = 71, + roundedSelection = 72, + rulers = 73, + scrollbar = 74, + scrollBeyondLastColumn = 75, + scrollBeyondLastLine = 76, + selectionClipboard = 77, + selectionHighlight = 78, + selectOnLineNumbers = 79, + showFoldingControls = 80, + showUnused = 81, + snippetSuggestions = 82, + smoothScrolling = 83, + stopRenderingLineAfter = 84, + suggest = 85, + suggestFontSize = 86, + suggestLineHeight = 87, + suggestOnTriggerCharacters = 88, + suggestSelection = 89, + tabCompletion = 90, + useTabStops = 91, + wordSeparators = 92, + wordWrap = 93, + wordWrapBreakAfterCharacters = 94, + wordWrapBreakBeforeCharacters = 95, + wordWrapBreakObtrusiveCharacters = 96, + wordWrapColumn = 97, + wordWrapMinified = 98, + wrappingIndent = 99, + editorClassName = 100, + pixelRatio = 101, + tabFocusMode = 102, + layoutInfo = 103, + wrappingInfo = 104 + } + export const EditorOptions: { + acceptSuggestionOnCommitCharacter: IEditorOption; + acceptSuggestionOnEnter: IEditorOption; + accessibilitySupport: IEditorOption; + accessibilityPageSize: IEditorOption; + ariaLabel: IEditorOption; + autoClosingBrackets: IEditorOption; + autoClosingOvertype: IEditorOption; + autoClosingQuotes: IEditorOption; + autoIndent: IEditorOption; + automaticLayout: IEditorOption; + autoSurround: IEditorOption; + codeLens: IEditorOption; + colorDecorators: IEditorOption; + contextmenu: IEditorOption; + copyWithSyntaxHighlighting: IEditorOption; + cursorBlinking: IEditorOption; + cursorSmoothCaretAnimation: IEditorOption; + cursorStyle: IEditorOption; + cursorSurroundingLines: IEditorOption; + cursorSurroundingLinesStyle: IEditorOption; + cursorWidth: IEditorOption; + disableLayerHinting: IEditorOption; + disableMonospaceOptimizations: IEditorOption; + dragAndDrop: IEditorOption; + emptySelectionClipboard: IEditorOption; + extraEditorClassName: IEditorOption; + fastScrollSensitivity: IEditorOption; + find: IEditorOption; + fixedOverflowWidgets: IEditorOption; + folding: IEditorOption; + foldingStrategy: IEditorOption; + fontFamily: IEditorOption; + fontInfo: IEditorOption; + fontLigatures2: IEditorOption; + fontSize: IEditorOption; + fontWeight: IEditorOption; + formatOnPaste: IEditorOption; + formatOnType: IEditorOption; + glyphMargin: IEditorOption; + gotoLocation: IEditorOption; + hideCursorInOverviewRuler: IEditorOption; + highlightActiveIndentGuide: IEditorOption; + hover: IEditorOption; + inDiffEditor: IEditorOption; + letterSpacing: IEditorOption; + lightbulb: IEditorOption; + lineDecorationsWidth: IEditorOption; + lineHeight: IEditorOption; + lineNumbers: IEditorOption; + lineNumbersMinChars: IEditorOption; + links: IEditorOption; + matchBrackets: IEditorOption; + minimap: IEditorOption; + mouseStyle: IEditorOption; + mouseWheelScrollSensitivity: IEditorOption; + mouseWheelZoom: IEditorOption; + multiCursorMergeOverlapping: IEditorOption; + multiCursorModifier: IEditorOption; + multiCursorPaste: IEditorOption; + occurrencesHighlight: IEditorOption; + overviewRulerBorder: IEditorOption; + overviewRulerLanes: IEditorOption; + parameterHints: IEditorOption; + quickSuggestions: IEditorOption; + quickSuggestionsDelay: IEditorOption; + readOnly: IEditorOption; + renderControlCharacters: IEditorOption; + renderIndentGuides: IEditorOption; + renderFinalNewline: IEditorOption; + renderLineHighlight: IEditorOption; + renderWhitespace: IEditorOption; + revealHorizontalRightPadding: IEditorOption; + roundedSelection: IEditorOption; + rulers: IEditorOption; + scrollbar: IEditorOption; + scrollBeyondLastColumn: IEditorOption; + scrollBeyondLastLine: IEditorOption; + selectionClipboard: IEditorOption; + selectionHighlight: IEditorOption; + selectOnLineNumbers: IEditorOption; + showFoldingControls: IEditorOption; + showUnused: IEditorOption; + snippetSuggestions: IEditorOption; + smoothScrolling: IEditorOption; + stopRenderingLineAfter: IEditorOption; + suggest: IEditorOption; + suggestFontSize: IEditorOption; + suggestLineHeight: IEditorOption; + suggestOnTriggerCharacters: IEditorOption; + suggestSelection: IEditorOption; + tabCompletion: IEditorOption; + useTabStops: IEditorOption; + wordSeparators: IEditorOption; + wordWrap: IEditorOption; + wordWrapBreakAfterCharacters: IEditorOption; + wordWrapBreakBeforeCharacters: IEditorOption; + wordWrapBreakObtrusiveCharacters: IEditorOption; + wordWrapColumn: IEditorOption; + wordWrapMinified: IEditorOption; + wrappingIndent: IEditorOption; + editorClassName: IEditorOption; + pixelRatio: IEditorOption; + tabFocusMode: IEditorOption; + layoutInfo: IEditorOption; + wrappingInfo: IEditorOption; + }; + + type EditorOptionsType = typeof EditorOptions; + + type FindEditorOptionsKeyById = { + [K in keyof EditorOptionsType]: EditorOptionsType[K]['id'] extends T ? K : never; + }[keyof EditorOptionsType]; + + type ComputedEditorOptionValue> = T extends IEditorOption ? R : never; + + export type FindComputedEditorOptionValueById = NonNullable]>>; + /** * A view zone is a full horizontal rectangle that 'pushes' text down. * The editor reserves space for view zones when rendering. @@ -3954,6 +4345,14 @@ declare namespace monaco.editor { * It is safe to call setModel(null) to simply detach the current model from the editor. */ setModel(model: ITextModel | null): void; + /** + * Gets all the editor computed options. + */ + getOptions(): IComputedEditorOptions; + /** + * Gets a specific editor option. + */ + getOption(id: T): FindComputedEditorOptionValueById; /** * Returns the editor's configuration (without any validation or defaults). */ @@ -5706,4 +6105,4 @@ declare namespace monaco.worker { } -//dtsv=2 +//dtsv=3 From 10603fe212d50a4be238fabc18d653acb5e13780 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 14 Jan 2020 16:31:00 +0100 Subject: [PATCH 269/843] Rerun monacodts on latest and replace " with ' in strings --- build/monaco/api.js | 9 + build/monaco/api.ts | 9 + .../common/standalone/standaloneEnums.ts | 150 ++++++++------- src/vs/monaco.d.ts | 182 +++++++++--------- 4 files changed, 187 insertions(+), 163 deletions(-) diff --git a/build/monaco/api.js b/build/monaco/api.js index ee9505c0595..0fbaf7335b7 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -171,6 +171,15 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, result = result.replace(/export default /g, 'export '); result = result.replace(/export declare /g, 'export '); result = result.replace(/declare /g, ''); + let lines = result.split(/\r\n|\r|\n/); + for (let i = 0; i < lines.length; i++) { + if (/\s*\*/.test(lines[i])) { + // very likely a comment + continue; + } + lines[i] = lines[i].replace(/"/g, '\''); + } + result = lines.join('\n'); if (declaration.kind === ts.SyntaxKind.EnumDeclaration) { result = result.replace(/const enum/, 'enum'); enums.push({ diff --git a/build/monaco/api.ts b/build/monaco/api.ts index b52b0b63431..511768ee64b 100644 --- a/build/monaco/api.ts +++ b/build/monaco/api.ts @@ -200,6 +200,15 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati result = result.replace(/export default /g, 'export '); result = result.replace(/export declare /g, 'export '); result = result.replace(/declare /g, ''); + let lines = result.split(/\r\n|\r|\n/); + for (let i = 0; i < lines.length; i++) { + if (/\s*\*/.test(lines[i])) { + // very likely a comment + continue; + } + lines[i] = lines[i].replace(/"/g, '\''); + } + result = lines.join('\n'); if (declaration.kind === ts.SyntaxKind.EnumDeclaration) { result = result.replace(/const enum/, 'enum'); diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index f23767462c3..3d436fb75bc 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -196,80 +196,82 @@ export enum EditorOption { fixedOverflowWidgets = 28, folding = 29, foldingStrategy = 30, - fontFamily = 31, - fontInfo = 32, - fontLigatures = 33, - fontSize = 34, - fontWeight = 35, - formatOnPaste = 36, - formatOnType = 37, - glyphMargin = 38, - gotoLocation = 39, - hideCursorInOverviewRuler = 40, - highlightActiveIndentGuide = 41, - hover = 42, - inDiffEditor = 43, - letterSpacing = 44, - lightbulb = 45, - lineDecorationsWidth = 46, - lineHeight = 47, - lineNumbers = 48, - lineNumbersMinChars = 49, - links = 50, - matchBrackets = 51, - minimap = 52, - mouseStyle = 53, - mouseWheelScrollSensitivity = 54, - mouseWheelZoom = 55, - multiCursorMergeOverlapping = 56, - multiCursorModifier = 57, - multiCursorPaste = 58, - occurrencesHighlight = 59, - overviewRulerBorder = 60, - overviewRulerLanes = 61, - parameterHints = 62, - quickSuggestions = 63, - quickSuggestionsDelay = 64, - readOnly = 65, - renderControlCharacters = 66, - renderIndentGuides = 67, - renderFinalNewline = 68, - renderLineHighlight = 69, - renderWhitespace = 70, - revealHorizontalRightPadding = 71, - roundedSelection = 72, - rulers = 73, - scrollbar = 74, - scrollBeyondLastColumn = 75, - scrollBeyondLastLine = 76, - selectionClipboard = 77, - selectionHighlight = 78, - selectOnLineNumbers = 79, - showFoldingControls = 80, - showUnused = 81, - snippetSuggestions = 82, - smoothScrolling = 83, - stopRenderingLineAfter = 84, - suggest = 85, - suggestFontSize = 86, - suggestLineHeight = 87, - suggestOnTriggerCharacters = 88, - suggestSelection = 89, - tabCompletion = 90, - useTabStops = 91, - wordSeparators = 92, - wordWrap = 93, - wordWrapBreakAfterCharacters = 94, - wordWrapBreakBeforeCharacters = 95, - wordWrapBreakObtrusiveCharacters = 96, - wordWrapColumn = 97, - wordWrapMinified = 98, - wrappingIndent = 99, - editorClassName = 100, - pixelRatio = 101, - tabFocusMode = 102, - layoutInfo = 103, - wrappingInfo = 104 + foldingHighlight = 31, + fontFamily = 32, + fontInfo = 33, + fontLigatures = 34, + fontSize = 35, + fontWeight = 36, + formatOnPaste = 37, + formatOnType = 38, + glyphMargin = 39, + gotoLocation = 40, + hideCursorInOverviewRuler = 41, + highlightActiveIndentGuide = 42, + hover = 43, + inDiffEditor = 44, + letterSpacing = 45, + lightbulb = 46, + lineDecorationsWidth = 47, + lineHeight = 48, + lineNumbers = 49, + lineNumbersMinChars = 50, + links = 51, + matchBrackets = 52, + minimap = 53, + mouseStyle = 54, + mouseWheelScrollSensitivity = 55, + mouseWheelZoom = 56, + multiCursorMergeOverlapping = 57, + multiCursorModifier = 58, + multiCursorPaste = 59, + occurrencesHighlight = 60, + overviewRulerBorder = 61, + overviewRulerLanes = 62, + parameterHints = 63, + peekWidgetFocusInlineEditor = 64, + quickSuggestions = 65, + quickSuggestionsDelay = 66, + readOnly = 67, + renderControlCharacters = 68, + renderIndentGuides = 69, + renderFinalNewline = 70, + renderLineHighlight = 71, + renderWhitespace = 72, + revealHorizontalRightPadding = 73, + roundedSelection = 74, + rulers = 75, + scrollbar = 76, + scrollBeyondLastColumn = 77, + scrollBeyondLastLine = 78, + selectionClipboard = 79, + selectionHighlight = 80, + selectOnLineNumbers = 81, + showFoldingControls = 82, + showUnused = 83, + snippetSuggestions = 84, + smoothScrolling = 85, + stopRenderingLineAfter = 86, + suggest = 87, + suggestFontSize = 88, + suggestLineHeight = 89, + suggestOnTriggerCharacters = 90, + suggestSelection = 91, + tabCompletion = 92, + useTabStops = 93, + wordSeparators = 94, + wordWrap = 95, + wordWrapBreakAfterCharacters = 96, + wordWrapBreakBeforeCharacters = 97, + wordWrapColumn = 98, + wordWrapMinified = 99, + wrappingIndent = 100, + wrappingAlgorithm = 101, + editorClassName = 102, + pixelRatio = 103, + tabFocusMode = 104, + layoutInfo = 105, + wrappingInfo = 106 } /** diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 14651914b4f..905bcd9cdc8 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3702,84 +3702,86 @@ declare namespace monaco.editor { fixedOverflowWidgets = 28, folding = 29, foldingStrategy = 30, - fontFamily = 31, - fontInfo = 32, - fontLigatures = 33, - fontSize = 34, - fontWeight = 35, - formatOnPaste = 36, - formatOnType = 37, - glyphMargin = 38, - gotoLocation = 39, - hideCursorInOverviewRuler = 40, - highlightActiveIndentGuide = 41, - hover = 42, - inDiffEditor = 43, - letterSpacing = 44, - lightbulb = 45, - lineDecorationsWidth = 46, - lineHeight = 47, - lineNumbers = 48, - lineNumbersMinChars = 49, - links = 50, - matchBrackets = 51, - minimap = 52, - mouseStyle = 53, - mouseWheelScrollSensitivity = 54, - mouseWheelZoom = 55, - multiCursorMergeOverlapping = 56, - multiCursorModifier = 57, - multiCursorPaste = 58, - occurrencesHighlight = 59, - overviewRulerBorder = 60, - overviewRulerLanes = 61, - parameterHints = 62, - quickSuggestions = 63, - quickSuggestionsDelay = 64, - readOnly = 65, - renderControlCharacters = 66, - renderIndentGuides = 67, - renderFinalNewline = 68, - renderLineHighlight = 69, - renderWhitespace = 70, - revealHorizontalRightPadding = 71, - roundedSelection = 72, - rulers = 73, - scrollbar = 74, - scrollBeyondLastColumn = 75, - scrollBeyondLastLine = 76, - selectionClipboard = 77, - selectionHighlight = 78, - selectOnLineNumbers = 79, - showFoldingControls = 80, - showUnused = 81, - snippetSuggestions = 82, - smoothScrolling = 83, - stopRenderingLineAfter = 84, - suggest = 85, - suggestFontSize = 86, - suggestLineHeight = 87, - suggestOnTriggerCharacters = 88, - suggestSelection = 89, - tabCompletion = 90, - useTabStops = 91, - wordSeparators = 92, - wordWrap = 93, - wordWrapBreakAfterCharacters = 94, - wordWrapBreakBeforeCharacters = 95, - wordWrapBreakObtrusiveCharacters = 96, - wordWrapColumn = 97, - wordWrapMinified = 98, - wrappingIndent = 99, - editorClassName = 100, - pixelRatio = 101, - tabFocusMode = 102, - layoutInfo = 103, - wrappingInfo = 104 + foldingHighlight = 31, + fontFamily = 32, + fontInfo = 33, + fontLigatures = 34, + fontSize = 35, + fontWeight = 36, + formatOnPaste = 37, + formatOnType = 38, + glyphMargin = 39, + gotoLocation = 40, + hideCursorInOverviewRuler = 41, + highlightActiveIndentGuide = 42, + hover = 43, + inDiffEditor = 44, + letterSpacing = 45, + lightbulb = 46, + lineDecorationsWidth = 47, + lineHeight = 48, + lineNumbers = 49, + lineNumbersMinChars = 50, + links = 51, + matchBrackets = 52, + minimap = 53, + mouseStyle = 54, + mouseWheelScrollSensitivity = 55, + mouseWheelZoom = 56, + multiCursorMergeOverlapping = 57, + multiCursorModifier = 58, + multiCursorPaste = 59, + occurrencesHighlight = 60, + overviewRulerBorder = 61, + overviewRulerLanes = 62, + parameterHints = 63, + peekWidgetFocusInlineEditor = 64, + quickSuggestions = 65, + quickSuggestionsDelay = 66, + readOnly = 67, + renderControlCharacters = 68, + renderIndentGuides = 69, + renderFinalNewline = 70, + renderLineHighlight = 71, + renderWhitespace = 72, + revealHorizontalRightPadding = 73, + roundedSelection = 74, + rulers = 75, + scrollbar = 76, + scrollBeyondLastColumn = 77, + scrollBeyondLastLine = 78, + selectionClipboard = 79, + selectionHighlight = 80, + selectOnLineNumbers = 81, + showFoldingControls = 82, + showUnused = 83, + snippetSuggestions = 84, + smoothScrolling = 85, + stopRenderingLineAfter = 86, + suggest = 87, + suggestFontSize = 88, + suggestLineHeight = 89, + suggestOnTriggerCharacters = 90, + suggestSelection = 91, + tabCompletion = 92, + useTabStops = 93, + wordSeparators = 94, + wordWrap = 95, + wordWrapBreakAfterCharacters = 96, + wordWrapBreakBeforeCharacters = 97, + wordWrapColumn = 98, + wordWrapMinified = 99, + wrappingIndent = 100, + wrappingAlgorithm = 101, + editorClassName = 102, + pixelRatio = 103, + tabFocusMode = 104, + layoutInfo = 105, + wrappingInfo = 106 } export const EditorOptions: { acceptSuggestionOnCommitCharacter: IEditorOption; - acceptSuggestionOnEnter: IEditorOption; + acceptSuggestionOnEnter: IEditorOption; accessibilitySupport: IEditorOption; accessibilityPageSize: IEditorOption; ariaLabel: IEditorOption; @@ -3797,7 +3799,7 @@ declare namespace monaco.editor { cursorSmoothCaretAnimation: IEditorOption; cursorStyle: IEditorOption; cursorSurroundingLines: IEditorOption; - cursorSurroundingLinesStyle: IEditorOption; + cursorSurroundingLinesStyle: IEditorOption; cursorWidth: IEditorOption; disableLayerHinting: IEditorOption; disableMonospaceOptimizations: IEditorOption; @@ -3808,7 +3810,8 @@ declare namespace monaco.editor { find: IEditorOption; fixedOverflowWidgets: IEditorOption; folding: IEditorOption; - foldingStrategy: IEditorOption; + foldingStrategy: IEditorOption; + foldingHighlight: IEditorOption; fontFamily: IEditorOption; fontInfo: IEditorOption; fontLigatures2: IEditorOption; @@ -3829,26 +3832,27 @@ declare namespace monaco.editor { lineNumbers: IEditorOption; lineNumbersMinChars: IEditorOption; links: IEditorOption; - matchBrackets: IEditorOption; + matchBrackets: IEditorOption; minimap: IEditorOption; - mouseStyle: IEditorOption; + mouseStyle: IEditorOption; mouseWheelScrollSensitivity: IEditorOption; mouseWheelZoom: IEditorOption; multiCursorMergeOverlapping: IEditorOption; - multiCursorModifier: IEditorOption; - multiCursorPaste: IEditorOption; + multiCursorModifier: IEditorOption; + multiCursorPaste: IEditorOption; occurrencesHighlight: IEditorOption; overviewRulerBorder: IEditorOption; overviewRulerLanes: IEditorOption; parameterHints: IEditorOption; + peekWidgetFocusInlineEditor: IEditorOption; quickSuggestions: IEditorOption; quickSuggestionsDelay: IEditorOption; readOnly: IEditorOption; renderControlCharacters: IEditorOption; renderIndentGuides: IEditorOption; renderFinalNewline: IEditorOption; - renderLineHighlight: IEditorOption; - renderWhitespace: IEditorOption; + renderLineHighlight: IEditorOption; + renderWhitespace: IEditorOption; revealHorizontalRightPadding: IEditorOption; roundedSelection: IEditorOption; rulers: IEditorOption; @@ -3858,26 +3862,26 @@ declare namespace monaco.editor { selectionClipboard: IEditorOption; selectionHighlight: IEditorOption; selectOnLineNumbers: IEditorOption; - showFoldingControls: IEditorOption; + showFoldingControls: IEditorOption; showUnused: IEditorOption; - snippetSuggestions: IEditorOption; + snippetSuggestions: IEditorOption; smoothScrolling: IEditorOption; stopRenderingLineAfter: IEditorOption; suggest: IEditorOption; suggestFontSize: IEditorOption; suggestLineHeight: IEditorOption; suggestOnTriggerCharacters: IEditorOption; - suggestSelection: IEditorOption; - tabCompletion: IEditorOption; + suggestSelection: IEditorOption; + tabCompletion: IEditorOption; useTabStops: IEditorOption; wordSeparators: IEditorOption; - wordWrap: IEditorOption; + wordWrap: IEditorOption; wordWrapBreakAfterCharacters: IEditorOption; wordWrapBreakBeforeCharacters: IEditorOption; - wordWrapBreakObtrusiveCharacters: IEditorOption; wordWrapColumn: IEditorOption; wordWrapMinified: IEditorOption; wrappingIndent: IEditorOption; + wrappingAlgorithm: IEditorOption; editorClassName: IEditorOption; pixelRatio: IEditorOption; tabFocusMode: IEditorOption; From b713669535346ae36f6ac91c845daf1966bc2301 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 14 Jan 2020 16:33:19 +0100 Subject: [PATCH 270/843] Rerun monacodts again --- .../common/standalone/standaloneEnums.ts | 51 +++++++++--------- src/vs/monaco.d.ts | 52 ++++++++++--------- 2 files changed, 53 insertions(+), 50 deletions(-) diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 3d436fb75bc..0d02283d1bb 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -247,31 +247,32 @@ export enum EditorOption { selectionClipboard = 79, selectionHighlight = 80, selectOnLineNumbers = 81, - showFoldingControls = 82, - showUnused = 83, - snippetSuggestions = 84, - smoothScrolling = 85, - stopRenderingLineAfter = 86, - suggest = 87, - suggestFontSize = 88, - suggestLineHeight = 89, - suggestOnTriggerCharacters = 90, - suggestSelection = 91, - tabCompletion = 92, - useTabStops = 93, - wordSeparators = 94, - wordWrap = 95, - wordWrapBreakAfterCharacters = 96, - wordWrapBreakBeforeCharacters = 97, - wordWrapColumn = 98, - wordWrapMinified = 99, - wrappingIndent = 100, - wrappingAlgorithm = 101, - editorClassName = 102, - pixelRatio = 103, - tabFocusMode = 104, - layoutInfo = 105, - wrappingInfo = 106 + semanticHighlighting = 82, + showFoldingControls = 83, + showUnused = 84, + snippetSuggestions = 85, + smoothScrolling = 86, + stopRenderingLineAfter = 87, + suggest = 88, + suggestFontSize = 89, + suggestLineHeight = 90, + suggestOnTriggerCharacters = 91, + suggestSelection = 92, + tabCompletion = 93, + useTabStops = 94, + wordSeparators = 95, + wordWrap = 96, + wordWrapBreakAfterCharacters = 97, + wordWrapBreakBeforeCharacters = 98, + wordWrapColumn = 99, + wordWrapMinified = 100, + wrappingIndent = 101, + wrappingAlgorithm = 102, + editorClassName = 103, + pixelRatio = 104, + tabFocusMode = 105, + layoutInfo = 106, + wrappingInfo = 107 } /** diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 905bcd9cdc8..3a6307fbbda 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3753,31 +3753,32 @@ declare namespace monaco.editor { selectionClipboard = 79, selectionHighlight = 80, selectOnLineNumbers = 81, - showFoldingControls = 82, - showUnused = 83, - snippetSuggestions = 84, - smoothScrolling = 85, - stopRenderingLineAfter = 86, - suggest = 87, - suggestFontSize = 88, - suggestLineHeight = 89, - suggestOnTriggerCharacters = 90, - suggestSelection = 91, - tabCompletion = 92, - useTabStops = 93, - wordSeparators = 94, - wordWrap = 95, - wordWrapBreakAfterCharacters = 96, - wordWrapBreakBeforeCharacters = 97, - wordWrapColumn = 98, - wordWrapMinified = 99, - wrappingIndent = 100, - wrappingAlgorithm = 101, - editorClassName = 102, - pixelRatio = 103, - tabFocusMode = 104, - layoutInfo = 105, - wrappingInfo = 106 + semanticHighlighting = 82, + showFoldingControls = 83, + showUnused = 84, + snippetSuggestions = 85, + smoothScrolling = 86, + stopRenderingLineAfter = 87, + suggest = 88, + suggestFontSize = 89, + suggestLineHeight = 90, + suggestOnTriggerCharacters = 91, + suggestSelection = 92, + tabCompletion = 93, + useTabStops = 94, + wordSeparators = 95, + wordWrap = 96, + wordWrapBreakAfterCharacters = 97, + wordWrapBreakBeforeCharacters = 98, + wordWrapColumn = 99, + wordWrapMinified = 100, + wrappingIndent = 101, + wrappingAlgorithm = 102, + editorClassName = 103, + pixelRatio = 104, + tabFocusMode = 105, + layoutInfo = 106, + wrappingInfo = 107 } export const EditorOptions: { acceptSuggestionOnCommitCharacter: IEditorOption; @@ -3862,6 +3863,7 @@ declare namespace monaco.editor { selectionClipboard: IEditorOption; selectionHighlight: IEditorOption; selectOnLineNumbers: IEditorOption; + semanticHighlighting: IEditorOption; showFoldingControls: IEditorOption; showUnused: IEditorOption; snippetSuggestions: IEditorOption; From b7381d341dce10f6e47b1b19410386932cf29104 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 14 Jan 2020 16:35:46 +0100 Subject: [PATCH 271/843] fix repl --- src/vs/workbench/contrib/debug/browser/repl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 1503ed1a983..d9381e21706 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -103,7 +103,7 @@ export class Repl extends ViewPane implements IPrivateReplService, IHistoryNavig constructor( options: IViewPaneOptions, @IDebugService private readonly debugService: IDebugService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IStorageService private readonly storageService: IStorageService, @IThemeService protected themeService: IThemeService, @IModelService private readonly modelService: IModelService, @@ -116,7 +116,7 @@ export class Repl extends ViewPane implements IPrivateReplService, IHistoryNavig @IEditorService private readonly editorService: IEditorService, @IKeybindingService keybindingService: IKeybindingService ) { - super({ ...(options as IViewPaneOptions), id: REPL_VIEW_ID, ariaHeaderLabel: localize('debugConsole', "Debug Console") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), id: REPL_VIEW_ID, ariaHeaderLabel: localize('debugConsole', "Debug Console") }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this.history = new HistoryNavigator(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')), 50); codeEditorService.registerDecorationType(DECORATION_KEY, {}); From 31a85df309f9e6958382c4d9b6d8a6e9594b9b16 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 14 Jan 2020 16:46:25 +0100 Subject: [PATCH 272/843] Avoid memory leak where the _markersData never has its elements removed --- src/vs/editor/common/services/markerDecorationsServiceImpl.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts index 44fd54c6c6d..d37fbe83d1e 100644 --- a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts +++ b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts @@ -38,7 +38,9 @@ class MarkerDecorations extends Disposable { } public update(markers: IMarker[], newDecorations: IModelDeltaDecoration[]): void { - const ids = this.model.deltaDecorations(keys(this._markersData), newDecorations); + const oldIds = keys(this._markersData); + this._markersData.clear(); + const ids = this.model.deltaDecorations(oldIds, newDecorations); for (let index = 0; index < ids.length; index++) { this._markersData.set(ids[index], markers[index]); } From 6133239792e1ad1dade13ce4b64cfe6a2599c749 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 14 Jan 2020 16:58:32 +0100 Subject: [PATCH 273/843] :lipstick: --- src/vs/editor/common/config/commonEditorConfig.ts | 6 +++--- .../editor/common/services/editorSimpleWorker.ts | 4 ++-- .../common/services/editorWorkerServiceImpl.ts | 6 +++--- src/vs/editor/common/viewLayout/viewLayout.ts | 14 +++++++------- src/vs/editor/common/viewModel/viewModelImpl.ts | 12 ++++++------ .../contrib/bracketMatching/bracketMatching.ts | 4 ++-- .../editor/contrib/codelens/codelensController.ts | 4 ++-- .../editor/contrib/comment/blockCommentCommand.ts | 10 +++++----- .../editor/contrib/comment/lineCommentCommand.ts | 12 ++++++------ src/vs/editor/contrib/dnd/dnd.ts | 6 +++--- src/vs/editor/contrib/dnd/dragAndDropCommand.ts | 8 ++++---- src/vs/editor/contrib/find/findController.ts | 4 ++-- src/vs/editor/contrib/find/findModel.ts | 8 ++++---- src/vs/editor/contrib/find/replaceAllCommand.ts | 8 ++++---- src/vs/editor/contrib/format/format.ts | 6 +++--- src/vs/editor/contrib/format/formatActions.ts | 6 +++--- src/vs/editor/contrib/gotoError/gotoError.ts | 4 ++-- .../gotoSymbol/link/goToDefinitionAtPosition.ts | 4 ++-- .../gotoSymbol/peek/referencesController.ts | 4 ++-- .../contrib/gotoSymbol/peek/referencesWidget.ts | 6 +++--- .../inPlaceReplace/inPlaceReplaceCommand.ts | 8 ++++---- .../contrib/linesOperations/copyLinesCommand.ts | 8 ++++---- .../contrib/linesOperations/sortLinesCommand.ts | 8 ++++---- src/vs/editor/contrib/links/links.ts | 4 ++-- src/vs/editor/contrib/message/messageController.ts | 6 +++--- .../contrib/wordHighlighter/wordHighlighter.ts | 4 ++-- .../browser/quickOpen/editorQuickOpen.ts | 8 ++++---- .../standalone/browser/quickOpen/gotoLine.ts | 10 +++++----- src/vs/editor/standalone/browser/simpleServices.ts | 8 ++++---- .../editor/standalone/browser/standaloneEditor.ts | 6 +++--- src/vs/editor/test/browser/testCodeEditor.ts | 6 +++--- src/vs/editor/test/browser/testCommand.ts | 10 +++++----- src/vs/workbench/api/browser/mainThreadEditor.ts | 12 ++++++------ .../browser/keybindingsEditorContribution.ts | 4 ++-- .../contrib/snippets/browser/tabCompletion.ts | 4 ++-- 35 files changed, 121 insertions(+), 121 deletions(-) diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index cee6665029f..a0b17085679 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -11,7 +11,7 @@ import * as arrays from 'vs/base/common/arrays'; import { IEditorOptions, editorOptionsRegistry, ValidatedEditorOptions, IEnvironmentalOptions, IComputedEditorOptions, ConfigurationChangedEvent, EDITOR_MODEL_DEFAULTS, EditorOption, FindComputedEditorOptionValueById } from 'vs/editor/common/config/editorOptions'; import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IConfiguration, IDimension } from 'vs/editor/common/editorCommon'; import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegistry, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; @@ -278,7 +278,7 @@ function deepCloneAndMigrateOptions(_options: IEditorOptions): IEditorOptions { return options; } -export abstract class CommonEditorConfiguration extends Disposable implements editorCommon.IConfiguration { +export abstract class CommonEditorConfiguration extends Disposable implements IConfiguration { private _onDidChange = this._register(new Emitter()); public readonly onDidChange: Event = this._onDidChange.event; @@ -308,7 +308,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed this._register(TabFocus.onDidChangeTabFocus(_ => this._recomputeOptions())); } - public observeReferenceElement(dimension?: editorCommon.IDimension): void { + public observeReferenceElement(dimension?: IDimension): void { } public dispose(): void { diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 4960aa3984a..3e5361ce764 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -13,7 +13,7 @@ import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { DiffComputer } from 'vs/editor/common/diff/diffComputer'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IChange } from 'vs/editor/common/editorCommon'; import { EndOfLineSequence, IWordAtPosition } from 'vs/editor/common/model'; import { IModelChangedEvent, MirrorTextModel as BaseMirrorModel } from 'vs/editor/common/model/mirrorTextModel'; import { ensureValidWordDefinition, getWordAtText } from 'vs/editor/common/model/wordHelper'; @@ -419,7 +419,7 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { return true; } - public async computeDirtyDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean): Promise { + public async computeDirtyDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean): Promise { let original = this._getModel(originalUrl); let modified = this._getModel(modifiedUrl); if (!original || !modified) { diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index e1e5804fdc0..1f2df5a96b7 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -10,7 +10,7 @@ import { SimpleWorkerClient, logOnceWebWorkerWarning, IWorkerClient } from 'vs/b import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IChange } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; @@ -90,7 +90,7 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker return (canSyncModel(this._modelService, original) && canSyncModel(this._modelService, modified)); } - public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise { + public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise { return this._workerManager.withWorker().then(client => client.computeDirtyDiff(original, modified, ignoreTrimWhitespace)); } @@ -437,7 +437,7 @@ export class EditorWorkerClient extends Disposable { }); } - public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise { + public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise { return this._withSyncedResources([original, modified]).then(proxy => { return proxy.computeDirtyDiff(original.toString(), modified.toString(), ignoreTrimWhitespace); }); diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index 115e4a1c85b..6ab6d10098b 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -5,9 +5,9 @@ import { Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility, INewScrollPosition } from 'vs/base/common/scrollable'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IConfiguration } from 'vs/editor/common/editorCommon'; import { LinesLayout, IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel'; @@ -16,13 +16,13 @@ const SMOOTH_SCROLLING_TIME = 125; export class ViewLayout extends Disposable implements IViewLayout { - private readonly _configuration: editorCommon.IConfiguration; + private readonly _configuration: IConfiguration; private readonly _linesLayout: LinesLayout; public readonly scrollable: Scrollable; public readonly onDidScroll: Event; - constructor(configuration: editorCommon.IConfiguration, lineCount: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { + constructor(configuration: IConfiguration, lineCount: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { super(); this._configuration = configuration; @@ -253,15 +253,15 @@ export class ViewLayout extends Disposable implements IViewLayout { return currentScrollPosition.scrollTop; } - public validateScrollPosition(scrollPosition: editorCommon.INewScrollPosition): IScrollPosition { + public validateScrollPosition(scrollPosition: INewScrollPosition): IScrollPosition { return this.scrollable.validateScrollPosition(scrollPosition); } - public setScrollPositionNow(position: editorCommon.INewScrollPosition): void { + public setScrollPositionNow(position: INewScrollPosition): void { this.scrollable.setScrollPositionNow(position); } - public setScrollPositionSmooth(position: editorCommon.INewScrollPosition): void { + public setScrollPositionSmooth(position: INewScrollPosition): void { this.scrollable.setScrollPositionSmooth(position); } diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 6bba786da59..75842518ee9 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -9,7 +9,7 @@ import * as strings from 'vs/base/common/strings'; import { ConfigurationChangedEvent, EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IConfiguration, IViewState } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference, IActiveIndentGuideInfo, ITextModel, TrackedRangeStickiness, TextModelResolvedOptions } from 'vs/editor/common/model'; import { ModelDecorationOverviewRulerOptions, ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel'; import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; @@ -30,7 +30,7 @@ const USE_IDENTITY_LINES_COLLECTION = true; export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel { private readonly editorId: number; - private readonly configuration: editorCommon.IConfiguration; + private readonly configuration: IConfiguration; private readonly model: ITextModel; private readonly _tokenizeViewportSoon: RunOnceScheduler; private hasFocus: boolean; @@ -44,7 +44,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel constructor( editorId: number, - configuration: editorCommon.IConfiguration, + configuration: IConfiguration, model: ITextModel, domLineBreaksComputerFactory: ILineBreaksComputerFactory, monospaceLineBreaksComputerFactory: ILineBreaksComputerFactory, @@ -447,7 +447,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel ); } - public saveState(): editorCommon.IViewState { + public saveState(): IViewState { const compatViewState = this.viewLayout.saveState(); const scrollTop = compatViewState.scrollTop; @@ -462,7 +462,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel }; } - public reduceRestoreState(state: editorCommon.IViewState): { scrollLeft: number; scrollTop: number; } { + public reduceRestoreState(state: IViewState): { scrollLeft: number; scrollTop: number; } { if (typeof state.firstPosition === 'undefined') { // This is a view state serialized by an older version return this._reduceRestoreStateCompatibility(state); @@ -477,7 +477,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel }; } - private _reduceRestoreStateCompatibility(state: editorCommon.IViewState): { scrollLeft: number; scrollTop: number; } { + private _reduceRestoreStateCompatibility(state: IViewState): { scrollLeft: number; scrollTop: number; } { return { scrollLeft: state.scrollLeft, scrollTop: state.scrollTopWithoutViewZones! diff --git a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts index a669b572023..5e150312ce6 100644 --- a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts @@ -13,7 +13,7 @@ import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorCon import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -103,7 +103,7 @@ class BracketsData { } } -export class BracketMatchingController extends Disposable implements editorCommon.IEditorContribution { +export class BracketMatchingController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.bracketMatchingController'; public static get(editor: ICodeEditor): BracketMatchingController { diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 40fca0ab3bd..7a644f1bfe4 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -9,7 +9,7 @@ import { toDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle import { StableEditorScrollState } from 'vs/editor/browser/core/editorState'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; import { CodeLensProviderRegistry, CodeLens } from 'vs/editor/common/modes'; import { CodeLensModel, getCodeLensData, CodeLensItem } from 'vs/editor/contrib/codelens/codelens'; @@ -21,7 +21,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { createStyleSheet } from 'vs/base/browser/dom'; import { hash } from 'vs/base/common/hash'; -export class CodeLensContribution implements editorCommon.IEditorContribution { +export class CodeLensContribution implements IEditorContribution { public static readonly ID: string = 'css.editor.codeLens'; diff --git a/src/vs/editor/contrib/comment/blockCommentCommand.ts b/src/vs/editor/contrib/comment/blockCommentCommand.ts index 5c5273c9c35..161c5878611 100644 --- a/src/vs/editor/contrib/comment/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/blockCommentCommand.ts @@ -8,11 +8,11 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -export class BlockCommentCommand implements editorCommon.ICommand { +export class BlockCommentCommand implements ICommand { private readonly _selection: Selection; private _usedEndToken: string | null; @@ -53,7 +53,7 @@ export class BlockCommentCommand implements editorCommon.ICommand { return true; } - private _createOperationsForBlockComment(selection: Range, startToken: string, endToken: string, model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + private _createOperationsForBlockComment(selection: Range, startToken: string, endToken: string, model: ITextModel, builder: IEditOperationBuilder): void { const startLineNumber = selection.startLineNumber; const startColumn = selection.startColumn; const endLineNumber = selection.endLineNumber; @@ -164,7 +164,7 @@ export class BlockCommentCommand implements editorCommon.ICommand { return res; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { const startLineNumber = this._selection.startLineNumber; const startColumn = this._selection.startColumn; @@ -179,7 +179,7 @@ export class BlockCommentCommand implements editorCommon.ICommand { this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, model, builder); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { const inverseEditOperations = helper.getInverseEditOperations(); if (inverseEditOperations.length === 2) { const startTokenEditOperation = inverseEditOperations[0]; diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index 2eecd5ae2c5..f6a3cc40766 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -9,7 +9,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand'; @@ -47,7 +47,7 @@ export const enum Type { ForceRemove = 2 } -export class LineCommentCommand implements editorCommon.ICommand { +export class LineCommentCommand implements ICommand { private readonly _selection: Selection; private _selectionId: string | null; @@ -187,7 +187,7 @@ export class LineCommentCommand implements editorCommon.ICommand { /** * Given a successful analysis, execute either insert line comments, either remove line comments */ - private _executeLineComments(model: ISimpleModel, builder: editorCommon.IEditOperationBuilder, data: IPreflightDataSupported, s: Selection): void { + private _executeLineComments(model: ISimpleModel, builder: IEditOperationBuilder, data: IPreflightDataSupported, s: Selection): void { let ops: IIdentifiedSingleEditOperation[]; @@ -266,7 +266,7 @@ export class LineCommentCommand implements editorCommon.ICommand { /** * Given an unsuccessful analysis, delegate to the block comment command */ - private _executeBlockComment(model: ITextModel, builder: editorCommon.IEditOperationBuilder, s: Selection): void { + private _executeBlockComment(model: ITextModel, builder: IEditOperationBuilder, s: Selection): void { model.tokenizeIfCheap(s.startLineNumber); let languageId = model.getLanguageIdAtPosition(s.startLineNumber, 1); let config = LanguageConfigurationRegistry.getComments(languageId); @@ -307,7 +307,7 @@ export class LineCommentCommand implements editorCommon.ICommand { } } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { let s = this._selection; this._moveEndPositionDown = false; @@ -325,7 +325,7 @@ export class LineCommentCommand implements editorCommon.ICommand { return this._executeBlockComment(model, builder, s); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { let result = helper.getTrackedSelection(this._selectionId!); if (this._moveEndPositionDown) { diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index 6d4226cbe5e..ea3cda654bc 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -10,7 +10,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ICodeEditor, IEditorMouseEvent, IMouseTarget, MouseTargetType, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -29,7 +29,7 @@ function hasTriggerModifier(e: IKeyboardEvent | IMouseEvent): boolean { } } -export class DragAndDropController extends Disposable implements editorCommon.IEditorContribution { +export class DragAndDropController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.dragAndDrop'; @@ -201,7 +201,7 @@ export class DragAndDropController extends Disposable implements editorCommon.IE }]; this._dndDecorationIds = this._editor.deltaDecorations(this._dndDecorationIds, newDecorations); - this._editor.revealPosition(position, editorCommon.ScrollType.Immediate); + this._editor.revealPosition(position, ScrollType.Immediate); } private _removeDecoration(): void { diff --git a/src/vs/editor/contrib/dnd/dragAndDropCommand.ts b/src/vs/editor/contrib/dnd/dragAndDropCommand.ts index 8d1d8a32b02..e6d7b427ae5 100644 --- a/src/vs/editor/contrib/dnd/dragAndDropCommand.ts +++ b/src/vs/editor/contrib/dnd/dragAndDropCommand.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { Selection } from 'vs/editor/common/core/selection'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; -export class DragAndDropCommand implements editorCommon.ICommand { +export class DragAndDropCommand implements ICommand { private readonly selection: Selection; private readonly targetPosition: Position; @@ -24,7 +24,7 @@ export class DragAndDropCommand implements editorCommon.ICommand { this.targetSelection = null; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { let text = model.getValueInRange(this.selection); if (!this.copy) { builder.addEditOperation(this.selection, null); @@ -102,7 +102,7 @@ export class DragAndDropCommand implements editorCommon.ICommand { } } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { return this.targetSelection!; } } diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index fbd9bf5500b..fee09b8429d 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -10,7 +10,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding, CONTEXT_REPLACE_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; import { FindOptionsWidget } from 'vs/editor/contrib/find/findOptionsWidget'; @@ -68,7 +68,7 @@ export interface IFindStartOptions { updateSearchScope: boolean; } -export class CommonFindController extends Disposable implements editorCommon.IEditorContribution { +export class CommonFindController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.findController'; diff --git a/src/vs/editor/contrib/find/findModel.ts b/src/vs/editor/contrib/find/findModel.ts index da67f86eb78..0653ac4f5c4 100644 --- a/src/vs/editor/contrib/find/findModel.ts +++ b/src/vs/editor/contrib/find/findModel.ts @@ -13,7 +13,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { Constants } from 'vs/base/common/uint'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ScrollType, ICommand } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference, FindMatch, ITextModel } from 'vs/editor/common/model'; import { SearchParams } from 'vs/editor/common/model/textModelSearch'; import { FindDecorations } from 'vs/editor/contrib/find/findDecorations'; @@ -209,7 +209,7 @@ export class FindModelBoundToEditorModel { let findScope = this._decorations.getFindScope(); if (findScope) { // Reveal the selection so user is reminded that 'selection find' is on. - this._editor.revealRangeInCenterIfOutsideViewport(findScope, editorCommon.ScrollType.Smooth); + this._editor.revealRangeInCenterIfOutsideViewport(findScope, ScrollType.Smooth); } return true; } @@ -225,7 +225,7 @@ export class FindModelBoundToEditorModel { ); this._editor.setSelection(match); - this._editor.revealRangeInCenterIfOutsideViewport(match, editorCommon.ScrollType.Smooth); + this._editor.revealRangeInCenterIfOutsideViewport(match, ScrollType.Smooth); } private _prevSearchPosition(before: Position) { @@ -536,7 +536,7 @@ export class FindModelBoundToEditorModel { this._editor.setSelections(selections); } - private _executeEditorCommand(source: string, command: editorCommon.ICommand): void { + private _executeEditorCommand(source: string, command: ICommand): void { try { this._ignoreModelContentChanged = true; this._editor.pushUndoStop(); diff --git a/src/vs/editor/contrib/find/replaceAllCommand.ts b/src/vs/editor/contrib/find/replaceAllCommand.ts index bdf2942f014..fb2de090a52 100644 --- a/src/vs/editor/contrib/find/replaceAllCommand.ts +++ b/src/vs/editor/contrib/find/replaceAllCommand.ts @@ -5,7 +5,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; interface IEditOperation { @@ -13,7 +13,7 @@ interface IEditOperation { text: string; } -export class ReplaceAllCommand implements editorCommon.ICommand { +export class ReplaceAllCommand implements ICommand { private readonly _editorSelection: Selection; private _trackedEditorSelectionId: string | null; @@ -27,7 +27,7 @@ export class ReplaceAllCommand implements editorCommon.ICommand { this._trackedEditorSelectionId = null; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { if (this._ranges.length > 0) { // Collect all edit operations let ops: IEditOperation[] = []; @@ -66,7 +66,7 @@ export class ReplaceAllCommand implements editorCommon.ICommand { this._trackedEditorSelectionId = builder.trackSelection(this._editorSelection); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { return helper.getTrackedSelection(this._trackedEditorSelectionId!); } } diff --git a/src/vs/editor/contrib/format/format.ts b/src/vs/editor/contrib/format/format.ts index 58602a5bfaf..6d87263ad51 100644 --- a/src/vs/editor/contrib/format/format.ts +++ b/src/vs/editor/contrib/format/format.ts @@ -14,7 +14,7 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ScrollType } from 'vs/editor/common/editorCommon'; import { ISingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { DocumentFormattingEditProvider, DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProvider, DocumentRangeFormattingEditProviderRegistry, FormattingOptions, OnTypeFormattingEditProviderRegistry, TextEdit } from 'vs/editor/common/modes'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; @@ -182,7 +182,7 @@ export async function formatDocumentRangeWithProvider( alertFormattingEdits(edits); editorOrModel.pushUndoStop(); editorOrModel.focus(); - editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), editorCommon.ScrollType.Immediate); + editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), ScrollType.Immediate); } else { // use model to apply edits @@ -272,7 +272,7 @@ export async function formatDocumentWithProvider( alertFormattingEdits(edits); editorOrModel.pushUndoStop(); editorOrModel.focus(); - editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), editorCommon.ScrollType.Immediate); + editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), ScrollType.Immediate); } } else { diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 879000c9140..61050ac167b 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -12,7 +12,7 @@ import { EditorAction, registerEditorAction, registerEditorContribution, Service import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; import { Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; @@ -26,7 +26,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { onUnexpectedError } from 'vs/base/common/errors'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -class FormatOnType implements editorCommon.IEditorContribution { +class FormatOnType implements IEditorContribution { public static readonly ID = 'editor.contrib.autoFormat'; @@ -149,7 +149,7 @@ class FormatOnType implements editorCommon.IEditorContribution { } } -class FormatOnPaste implements editorCommon.IEditorContribution { +class FormatOnPaste implements IEditorContribution { public static readonly ID = 'editor.contrib.formatOnPaste'; diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 11e2b470510..10f48cad8bb 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -12,7 +12,7 @@ import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/cont import { IMarker, IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, IActionOptions, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -189,7 +189,7 @@ class MarkerModel { } } -export class MarkerController implements editorCommon.IEditorContribution { +export class MarkerController implements IEditorContribution { public static readonly ID = 'editor.contrib.markerController'; diff --git a/src/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition.ts b/src/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition.ts index 240bf4c52df..d3678e527d9 100644 --- a/src/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition.ts +++ b/src/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition.ts @@ -11,7 +11,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { IModeService } from 'vs/editor/common/services/modeService'; import { Range, IRange } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { DefinitionProviderRegistry, LocationLink } from 'vs/editor/common/modes'; import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; @@ -28,7 +28,7 @@ import { Position } from 'vs/editor/common/core/position'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -export class GotoDefinitionAtPositionEditorContribution implements editorCommon.IEditorContribution { +export class GotoDefinitionAtPositionEditorContribution implements IEditorContribution { public static readonly ID = 'editor.contrib.gotodefinitionatposition'; static readonly MAX_SOURCE_PREVIEW_LINES = 8; diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts index be8af636d69..60f7428d6cf 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts @@ -11,7 +11,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { IContextKey, IContextKeyService, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ReferencesModel, OneReference } from '../referencesModel'; import { ReferenceWidget, LayoutData } from './referencesWidget'; @@ -29,7 +29,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; export const ctxReferenceSearchVisible = new RawContextKey('referenceSearchVisible', false); -export abstract class ReferencesController implements editorCommon.IEditorContribution { +export abstract class ReferencesController implements IEditorContribution { static readonly ID = 'editor.contrib.referencesController'; diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts index 10211eb0abc..f4d1f08555b 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts @@ -17,7 +17,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IRange, Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ScrollType } from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions, TextModel } from 'vs/editor/common/model/textModel'; import { Location } from 'vs/editor/common/modes'; @@ -247,7 +247,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { } show(where: IRange) { - this.editor.revealRangeInCenterIfOutsideViewport(where, editorCommon.ScrollType.Smooth); + this.editor.revealRangeInCenterIfOutsideViewport(where, ScrollType.Smooth); super.show(where, this.layoutData.heightInLines || 18); } @@ -526,7 +526,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { // show in editor const model = ref.object; if (model) { - const scrollType = this._preview.getModel() === model.textEditorModel ? editorCommon.ScrollType.Smooth : editorCommon.ScrollType.Immediate; + const scrollType = this._preview.getModel() === model.textEditorModel ? ScrollType.Smooth : ScrollType.Immediate; const sel = Range.lift(reference.range).collapseToStart(); this._previewModelReference = ref; this._preview.setModel(model.textEditorModel); diff --git a/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts b/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts index a64be8e0109..0d069f4f6c3 100644 --- a/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts +++ b/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; -export class InPlaceReplaceCommand implements editorCommon.ICommand { +export class InPlaceReplaceCommand implements ICommand { private readonly _editRange: Range; private readonly _originalSelection: Selection; @@ -20,11 +20,11 @@ export class InPlaceReplaceCommand implements editorCommon.ICommand { this._text = text; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { builder.addTrackedEditOperation(this._editRange, this._text); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { const inverseEditOperations = helper.getInverseEditOperations(); const srcRange = inverseEditOperations[0].range; diff --git a/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts b/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts index 3d207820061..6b76a75b763 100644 --- a/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts @@ -5,10 +5,10 @@ import { Range } from 'vs/editor/common/core/range'; import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -export class CopyLinesCommand implements editorCommon.ICommand { +export class CopyLinesCommand implements ICommand { private readonly _selection: Selection; private readonly _isCopyingDown: boolean; @@ -27,7 +27,7 @@ export class CopyLinesCommand implements editorCommon.ICommand { this._endLineNumberDelta = 0; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { let s = this._selection; this._startLineNumberDelta = 0; @@ -61,7 +61,7 @@ export class CopyLinesCommand implements editorCommon.ICommand { this._selectionDirection = this._selection.getDirection(); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { let result = helper.getTrackedSelection(this._selectionId!); if (this._startLineNumberDelta !== 0 || this._endLineNumberDelta !== 0) { diff --git a/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts b/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts index a201370cea3..0a1bc6bf90c 100644 --- a/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts @@ -6,10 +6,10 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; -export class SortLinesCommand implements editorCommon.ICommand { +export class SortLinesCommand implements ICommand { private static _COLLATOR: Intl.Collator | null = null; public static getCollator(): Intl.Collator { @@ -29,7 +29,7 @@ export class SortLinesCommand implements editorCommon.ICommand { this.selectionId = null; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { let op = sortLines(model, this.selection, this.descending); if (op) { builder.addEditOperation(op.range, op.text); @@ -38,7 +38,7 @@ export class SortLinesCommand implements editorCommon.ICommand { this.selectionId = builder.trackSelection(this.selection); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { return helper.getTrackedSelection(this.selectionId!); } diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index 6bce7e78d8d..bf5399f274a 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -14,7 +14,7 @@ import * as platform from 'vs/base/common/platform'; import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { LinkProviderRegistry } from 'vs/editor/common/modes'; @@ -97,7 +97,7 @@ class LinkOccurrence { } } -class LinkDetector implements editorCommon.IEditorContribution { +class LinkDetector implements IEditorContribution { public static readonly ID: string = 'editor.linkDetector'; diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index badf5eb2d6a..a4fcb5f2d1a 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -10,7 +10,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { IDisposable, Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { registerEditorContribution, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor, IContentWidget, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -19,7 +19,7 @@ import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/com import { inputValidationInfoBorder, inputValidationInfoBackground, inputValidationInfoForeground } from 'vs/platform/theme/common/colorRegistry'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -export class MessageController extends Disposable implements editorCommon.IEditorContribution { +export class MessageController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.messageController'; @@ -144,7 +144,7 @@ class MessageWidget implements IContentWidget { constructor(editor: ICodeEditor, { lineNumber, column }: IPosition, text: string) { this._editor = editor; - this._editor.revealLinesInCenterIfOutsideViewport(lineNumber, lineNumber, editorCommon.ScrollType.Smooth); + this._editor.revealLinesInCenterIfOutsideViewport(lineNumber, lineNumber, ScrollType.Smooth); this._position = { lineNumber, column: column - 1 }; this._domNode = document.createElement('div'); diff --git a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts index e4200b5dcfa..d2af95af60d 100644 --- a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts @@ -16,7 +16,7 @@ import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/commo import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IModelDeltaDecoration, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -456,7 +456,7 @@ class WordHighlighter { } } -class WordHighlighterContribution extends Disposable implements editorCommon.IEditorContribution { +class WordHighlighterContribution extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.wordHighlighter'; diff --git a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts b/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts index bee2ce31a26..b5d6aea1470 100644 --- a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts +++ b/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts @@ -10,7 +10,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, IActionOptions, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution, ScrollType, IEditor } from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { QuickOpenEditorWidget } from 'vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget'; @@ -22,7 +22,7 @@ export interface IQuickOpenControllerOpts { getAutoFocus(searchValue: string): IAutoFocus; } -export class QuickOpenController implements editorCommon.IEditorContribution, IDecorator { +export class QuickOpenController implements IEditorContribution, IDecorator { public static readonly ID = 'editor.controller.quickOpenController'; @@ -61,7 +61,7 @@ export class QuickOpenController implements editorCommon.IEditorContribution, ID // Restore selection if canceled if (canceled && this.lastKnownEditorSelection) { this.editor.setSelection(this.lastKnownEditorSelection); - this.editor.revealRangeInCenterIfOutsideViewport(this.lastKnownEditorSelection, editorCommon.ScrollType.Smooth); + this.editor.revealRangeInCenterIfOutsideViewport(this.lastKnownEditorSelection, ScrollType.Smooth); } this.lastKnownEditorSelection = null; @@ -165,7 +165,7 @@ export abstract class BaseEditorQuickOpenAction extends EditorAction { } export interface IDecorator { - decorateLine(range: Range, editor: editorCommon.IEditor): void; + decorateLine(range: Range, editor: IEditor): void; clearDecorations(): void; } diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts index 5d2960a5497..5274a3ae2c3 100644 --- a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts +++ b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts @@ -12,7 +12,7 @@ import { ICodeEditor, IDiffEditor, isCodeEditor } from 'vs/editor/browser/editor import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditor, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ITextModel } from 'vs/editor/common/model'; import { BaseEditorQuickOpenAction, IDecorator } from 'vs/editor/standalone/browser/quickOpen/editorQuickOpen'; @@ -28,9 +28,9 @@ interface ParseResult { export class GotoLineEntry extends QuickOpenEntry { private readonly parseResult: ParseResult; private readonly decorator: IDecorator; - private readonly editor: editorCommon.IEditor; + private readonly editor: IEditor; - constructor(line: string, editor: editorCommon.IEditor, decorator: IDecorator) { + constructor(line: string, editor: IEditor, decorator: IDecorator) { super(); this.editor = editor; @@ -108,7 +108,7 @@ export class GotoLineEntry extends QuickOpenEntry { // Apply selection and focus const range = this.toSelection(); (this.editor).setSelection(range); - (this.editor).revealRangeInCenter(range, editorCommon.ScrollType.Smooth); + (this.editor).revealRangeInCenter(range, ScrollType.Smooth); this.editor.focus(); return true; @@ -124,7 +124,7 @@ export class GotoLineEntry extends QuickOpenEntry { // Select Line Position const range = this.toSelection(); - this.editor.revealRangeInCenter(range, editorCommon.ScrollType.Smooth); + this.editor.revealRangeInCenter(range, ScrollType.Smooth); // Decorate if possible this.decorator.decorateLine(range, this.editor); diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 19a2aead6c3..2f2419ca6a8 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -18,7 +18,7 @@ import { isDiffEditorConfigurationKey, isEditorConfigurationKey } from 'vs/edito import { EditOperation } from 'vs/editor/common/core/editOperation'; import { IPosition, Position as Pos } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditor } from 'vs/editor/common/editorCommon'; import { ITextModel, ITextSnapshot } from 'vs/editor/common/model'; import { TextEdit, WorkspaceEdit, isResourceTextEdit } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -90,7 +90,7 @@ export interface IOpenEditorDelegate { (url: string): boolean; } -function withTypedEditor(widget: editorCommon.IEditor, codeEditorCallback: (editor: ICodeEditor) => T, diffEditorCallback: (editor: IDiffEditor) => T): T { +function withTypedEditor(widget: IEditor, codeEditorCallback: (editor: ICodeEditor) => T, diffEditorCallback: (editor: IDiffEditor) => T): T { if (isCodeEditor(widget)) { // Single Editor return codeEditorCallback(widget); @@ -104,13 +104,13 @@ export class SimpleEditorModelResolverService implements ITextModelService { public _serviceBrand: undefined; private readonly modelService: IModelService | undefined; - private editor?: editorCommon.IEditor; + private editor?: IEditor; constructor(modelService: IModelService | undefined) { this.modelService = modelService; } - public setEditor(editor: editorCommon.IEditor): void { + public setEditor(editor: IEditor): void { this.editor = editor; } diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 65285eb9a2d..e35ab13d4f4 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -13,7 +13,7 @@ import { DiffNavigator, IDiffNavigator } from 'vs/editor/browser/widget/diffNavi import { EditorOptions, ConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; import { Token } from 'vs/editor/common/core/token'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditor, EditorType } from 'vs/editor/common/editorCommon'; import { FindMatch, ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { NULL_STATE, nullTokenize } from 'vs/editor/common/modes/nullMode'; @@ -42,7 +42,7 @@ import { IEditorProgressService } from 'vs/platform/progress/common/progress'; type Omit = Pick>; -function withAllStandaloneServices(domElement: HTMLElement, override: IEditorOverrideServices, callback: (services: DynamicStandaloneServices) => T): T { +function withAllStandaloneServices(domElement: HTMLElement, override: IEditorOverrideServices, callback: (services: DynamicStandaloneServices) => T): T { let services = new DynamicStandaloneServices(domElement, override); let simpleEditorModelResolverService: SimpleEditorModelResolverService | null = null; @@ -375,7 +375,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor { FindMatch: FindMatch, // vars - EditorType: editorCommon.EditorType, + EditorType: EditorType, EditorOptions: EditorOptions }; diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index c4eb0282b2c..8626630e183 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -9,7 +9,7 @@ import { View } from 'vs/editor/browser/view/viewImpl'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; import * as editorOptions from 'vs/editor/common/config/editorOptions'; import { Cursor } from 'vs/editor/common/controller/cursor'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IConfiguration, IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; @@ -29,7 +29,7 @@ import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService export class TestCodeEditor extends CodeEditorWidget implements editorBrowser.ICodeEditor { //#region testing overrides - protected _createConfiguration(options: editorOptions.IEditorConstructionOptions): editorCommon.IConfiguration { + protected _createConfiguration(options: editorOptions.IEditorConstructionOptions): IConfiguration { return new TestConfiguration(options); } protected _createView(viewModel: ViewModel, cursor: Cursor): [View, boolean] { @@ -42,7 +42,7 @@ export class TestCodeEditor extends CodeEditorWidget implements editorBrowser.IC public getCursor(): Cursor | undefined { return this._modelData ? this._modelData.cursor : undefined; } - public registerAndInstantiateContribution(id: string, ctor: any): T { + public registerAndInstantiateContribution(id: string, ctor: any): T { let r = this._instantiationService.createInstance(ctor, this); this._contributions[id] = r; return r; diff --git a/src/vs/editor/test/browser/testCommand.ts b/src/vs/editor/test/browser/testCommand.ts index 5b96fde1e00..ce33b94a913 100644 --- a/src/vs/editor/test/browser/testCommand.ts +++ b/src/vs/editor/test/browser/testCommand.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, Handler, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageIdentifier } from 'vs/editor/common/modes'; @@ -16,7 +16,7 @@ export function testCommand( lines: string[], languageIdentifier: LanguageIdentifier | null, selection: Selection, - commandFactory: (selection: Selection) => editorCommon.ICommand, + commandFactory: (selection: Selection) => ICommand, expectedLines: string[], expectedSelection: Selection, forceTokenization?: boolean @@ -33,7 +33,7 @@ export function testCommand( cursor.setSelections('tests', [selection]); - cursor.trigger('tests', editorCommon.Handler.ExecuteCommand, commandFactory(cursor.getSelection())); + cursor.trigger('tests', Handler.ExecuteCommand, commandFactory(cursor.getSelection())); assert.deepEqual(model.getLinesContent(), expectedLines); @@ -47,9 +47,9 @@ export function testCommand( /** * Extract edit operations if command `command` were to execute on model `model` */ -export function getEditOperation(model: ITextModel, command: editorCommon.ICommand): IIdentifiedSingleEditOperation[] { +export function getEditOperation(model: ITextModel, command: ICommand): IIdentifiedSingleEditOperation[] { let operations: IIdentifiedSingleEditOperation[] = []; - let editOperationBuilder: editorCommon.IEditOperationBuilder = { + let editOperationBuilder: IEditOperationBuilder = { addEditOperation: (range: Range, text: string) => { operations.push({ range: range, diff --git a/src/vs/workbench/api/browser/mainThreadEditor.ts b/src/vs/workbench/api/browser/mainThreadEditor.ts index 239f87096f1..4de14d85d61 100644 --- a/src/vs/workbench/api/browser/mainThreadEditor.ts +++ b/src/vs/workbench/api/browser/mainThreadEditor.ts @@ -9,7 +9,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { RenderLineNumbersType, TextEditorCursorStyle, cursorStyleToString, EditorOption } from 'vs/editor/common/config/editorOptions'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IDecorationOptions, ScrollType } from 'vs/editor/common/editorCommon'; import { IIdentifiedSingleEditOperation, ISingleEditOperation, ITextModel, ITextModelUpdateOptions } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; @@ -365,7 +365,7 @@ export class MainThreadTextEditor { } } - public setDecorations(key: string, ranges: editorCommon.IDecorationOptions[]): void { + public setDecorations(key: string, ranges: IDecorationOptions[]): void { if (!this._codeEditor) { return; } @@ -389,16 +389,16 @@ export class MainThreadTextEditor { } switch (revealType) { case TextEditorRevealType.Default: - this._codeEditor.revealRange(range, editorCommon.ScrollType.Smooth); + this._codeEditor.revealRange(range, ScrollType.Smooth); break; case TextEditorRevealType.InCenter: - this._codeEditor.revealRangeInCenter(range, editorCommon.ScrollType.Smooth); + this._codeEditor.revealRangeInCenter(range, ScrollType.Smooth); break; case TextEditorRevealType.InCenterIfOutsideViewport: - this._codeEditor.revealRangeInCenterIfOutsideViewport(range, editorCommon.ScrollType.Smooth); + this._codeEditor.revealRangeInCenterIfOutsideViewport(range, ScrollType.Smooth); break; case TextEditorRevealType.AtTop: - this._codeEditor.revealRangeAtTop(range, editorCommon.ScrollType.Smooth); + this._codeEditor.revealRangeAtTop(range, ScrollType.Smooth); break; default: console.warn(`Unknown revealType: ${revealType}`); diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts index 8d8eb232266..77f48d1ec61 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts @@ -12,7 +12,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { registerEditorContribution, ServicesAccessor, registerEditorCommand, EditorCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; @@ -37,7 +37,7 @@ const NLS_KB_LAYOUT_ERROR_MESSAGE = nls.localize('defineKeybinding.kbLayoutError const INTERESTING_FILE = /keybindings\.json$/; -export class DefineKeybindingController extends Disposable implements editorCommon.IEditorContribution { +export class DefineKeybindingController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.defineKeybinding'; diff --git a/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts b/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts index 4f54adf2301..227169377e9 100644 --- a/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts +++ b/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts @@ -10,7 +10,7 @@ import { ISnippetsService } from './snippets.contribution'; import { getNonWhitespacePrefix } from './snippetsService'; import { endsWith } from 'vs/base/common/strings'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { Range } from 'vs/editor/common/core/range'; import { registerEditorContribution, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; @@ -21,7 +21,7 @@ import { Snippet } from './snippetsFile'; import { SnippetCompletion } from './snippetCompletionProvider'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -export class TabCompletionController implements editorCommon.IEditorContribution { +export class TabCompletionController implements IEditorContribution { public static readonly ID = 'editor.tabCompletionController'; static readonly ContextKey = new RawContextKey('hasSnippetCompletions', undefined); From 66fa2c0d2133657c885eb31602f21da420666423 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Tue, 14 Jan 2020 16:59:02 +0100 Subject: [PATCH 274/843] Extension id format (microsoft/vscode-remote-release#1800) --- .../configuration-editing/schemas/attachContainer.schema.json | 4 +++- .../configuration-editing/schemas/devContainer.schema.json | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/extensions/configuration-editing/schemas/attachContainer.schema.json b/extensions/configuration-editing/schemas/attachContainer.schema.json index cebd0f2bc3e..58e59d72ee0 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -40,7 +40,9 @@ "type": "array", "description": "An array of extensions that should be installed into the container.", "items": { - "type": "string" + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)$", + "errorMessage": "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'." } } } diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index aed58455d01..c9c34af63fa 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -15,7 +15,9 @@ "type": "array", "description": "An array of extensions that should be installed into the container.", "items": { - "type": "string" + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)$", + "errorMessage": "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'." } }, "settings": { From 1faf9a62214971256b5501e329ad6aa837e413a9 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 14 Jan 2020 17:11:07 +0100 Subject: [PATCH 275/843] debugStart: add telemetry --- .../contrib/debug/browser/startView.ts | 55 +++++++++++++------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/startView.ts b/src/vs/workbench/contrib/debug/browser/startView.ts index d45f2e4700c..cd6218472de 100644 --- a/src/vs/workbench/contrib/debug/browser/startView.ts +++ b/src/vs/workbench/contrib/debug/browser/startView.ts @@ -24,8 +24,31 @@ import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/vie import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const $ = dom.$; +interface DebugStartMetrics { + debuggers?: string[]; +} +type DebugStartMetricsClassification = { + debuggers?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; +}; + +function createClickElement(textContent: string, action: () => any): HTMLSpanElement { + const clickElement = $('span.click'); + clickElement.textContent = textContent; + clickElement.onclick = action; + clickElement.tabIndex = 0; + clickElement.onkeyup = (e) => { + const keyboardEvent = new StandardKeyboardEvent(e); + if (keyboardEvent.keyCode === KeyCode.Enter || (keyboardEvent.keyCode === KeyCode.Space)) { + action(); + } + }; + + return clickElement; +} + export class StartView extends ViewPane { static ID = 'workbench.debug.startView'; @@ -50,6 +73,7 @@ export class StartView extends ViewPane { @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IFileDialogService private readonly dialogService: IFileDialogService, @IInstantiationService instantiationService: IInstantiationService, + @ITelemetryService private readonly telemetryService: ITelemetryService ) { super({ ...(options as IViewPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); this._register(editorService.onDidActiveEditorChange(() => this.updateView())); @@ -79,12 +103,18 @@ export class StartView extends ViewPane { const setSecondMessage = () => { secondMessageElement.textContent = localize('specifyHowToRun', "To customize Run and Debug"); - this.clickElement = this.createClickElement(localize('configure', " create a launch.json file."), () => this.commandService.executeCommand(ConfigureAction.ID)); + this.clickElement = createClickElement(localize('configure', " create a launch.json file."), () => { + this.telemetryService.publicLog2('debugStart.configure', { debuggers: this.debuggerLabels }); + this.commandService.executeCommand(ConfigureAction.ID); + }); this.secondMessageContainer.appendChild(this.clickElement); }; const setSecondMessageWithFolder = () => { secondMessageElement.textContent = localize('noLaunchConfiguration', "To customize Run and Debug, "); - this.clickElement = this.createClickElement(localize('openFolder', " open a folder"), () => this.dialogService.pickFolderAndOpen({ forceNewWindow: false })); + this.clickElement = createClickElement(localize('openFolder', " open a folder"), () => { + this.telemetryService.publicLog2('debugStart.openFolder', { debuggers: this.debuggerLabels }); + this.dialogService.pickFolderAndOpen({ forceNewWindow: false }); + }); this.secondMessageContainer.appendChild(this.clickElement); const moreText = $('span.moreText'); @@ -109,7 +139,10 @@ export class StartView extends ViewPane { } if (!enabled && emptyWorkbench) { - this.clickElement = this.createClickElement(localize('openFile', "Open a file"), () => this.dialogService.pickFileAndOpen({ forceNewWindow: false })); + this.clickElement = createClickElement(localize('openFile', "Open a file"), () => { + this.telemetryService.publicLog2('debugStart.openFile'); + this.dialogService.pickFileAndOpen({ forceNewWindow: false }); + }); this.firstMessageContainer.appendChild(this.clickElement); const firstMessageElement = $('span'); this.firstMessageContainer.appendChild(firstMessageElement); @@ -120,21 +153,6 @@ export class StartView extends ViewPane { } } - private createClickElement(textContent: string, action: () => any): HTMLSpanElement { - const clickElement = $('span.click'); - clickElement.textContent = textContent; - clickElement.onclick = action; - clickElement.tabIndex = 0; - clickElement.onkeyup = (e) => { - const keyboardEvent = new StandardKeyboardEvent(e); - if (keyboardEvent.keyCode === KeyCode.Enter || (keyboardEvent.keyCode === KeyCode.Space)) { - action(); - } - }; - - return clickElement; - } - protected renderBody(container: HTMLElement): void { this.firstMessageContainer = $('.top-section'); container.appendChild(this.firstMessageContainer); @@ -142,6 +160,7 @@ export class StartView extends ViewPane { this.debugButton = new Button(container); this._register(this.debugButton.onDidClick(() => { this.commandService.executeCommand(StartAction.ID); + this.telemetryService.publicLog2('debugStart.runAndDebug', { debuggers: this.debuggerLabels }); })); attachButtonStyler(this.debugButton, this.themeService); From 7fa04ddceaab62c6dfed69eaf1fcd38c0ba4a2e1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 14 Jan 2020 16:40:48 +0100 Subject: [PATCH 276/843] #85216 pull and push support in synchronisers --- .../userDataSync/common/extensionsSync.ts | 97 ++++++++++++++--- .../userDataSync/common/globalStateSync.ts | 88 +++++++++++++-- .../userDataSync/common/keybindingsSync.ts | 96 ++++++++++++++-- .../userDataSync/common/settingsMerge.ts | 11 +- .../userDataSync/common/settingsSync.ts | 103 ++++++++++++++++-- .../userDataSync/common/userDataSync.ts | 2 + .../userDataSync/common/userDataSyncIpc.ts | 4 + .../common/userDataSyncService.ts | 32 ++++++ .../test/common/settingsMerge.test.ts | 6 +- .../electron-browser/settingsSyncService.ts | 8 ++ .../electron-browser/userDataSyncService.ts | 8 ++ 11 files changed, 401 insertions(+), 54 deletions(-) diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 98beb932ced..d8d83228bbd 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -19,11 +19,13 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { localize } from 'vs/nls'; import { merge } from 'vs/platform/userDataSync/common/extensionsMerge'; -export interface ISyncPreviewResult { +interface ISyncPreviewResult { readonly added: ISyncExtension[]; - readonly removed: ISyncExtension[]; + readonly removed: IExtensionIdentifier[]; readonly updated: ISyncExtension[]; readonly remote: ISyncExtension[] | null; + readonly remoteUserData: IUserData | null; + readonly skippedExtensions: ISyncExtension[]; } interface ILastSyncUserData extends IUserData { @@ -72,6 +74,61 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser } } + async pull(): Promise { + if (!this.configurationService.getValue('sync.enableExtensions')) { + this.logService.info('Extensions: Skipped pulling extensions as it is disabled.'); + return; + } + + this.stop(); + + try { + this.logService.info('Extensions: Started pulling extensions...'); + this.setStatus(SyncStatus.Syncing); + + const remoteUserData = await this.getRemoteUserData(); + + if (remoteUserData.content !== null) { + const localExtensions = await this.getLocalExtensions(); + const remoteExtensions: ISyncExtension[] = JSON.parse(remoteUserData.content); + const { added, updated, remote } = merge(localExtensions, remoteExtensions, [], [], this.getIgnoredExtensions()); + await this.apply({ added, removed: [], updated, remote, remoteUserData, skippedExtensions: [] }); + } + + // No remote exists to pull + else { + this.logService.info('Extensions: Remote extensions does not exist.'); + } + + this.logService.info('Extensions: Finished pulling extensions.'); + } finally { + this.setStatus(SyncStatus.Idle); + } + } + + async push(): Promise { + if (!this.configurationService.getValue('sync.enableExtensions')) { + this.logService.info('Extensions: Skipped pushing extensions as it is disabled.'); + return; + } + + this.stop(); + + try { + this.logService.info('Extensions: Started pushing extensions...'); + this.setStatus(SyncStatus.Syncing); + + const localExtensions = await this.getLocalExtensions(); + const { added, removed, updated, remote } = merge(localExtensions, null, null, [], this.getIgnoredExtensions()); + await this.apply({ added, removed, updated, remote, remoteUserData: null, skippedExtensions: [] }); + + this.logService.info('Extensions: Finished pulling extensions.'); + } finally { + this.setStatus(SyncStatus.Idle); + } + + } + async sync(): Promise { if (!this.configurationService.getValue('sync.enableExtensions')) { this.logService.trace('Extensions: Skipping synchronizing extensions as it is disabled.'); @@ -90,7 +147,8 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser this.setStatus(SyncStatus.Syncing); try { - await this.doSync(); + const previewResult = await this.getPreview(); + await this.apply(previewResult); } catch (e) { this.setStatus(SyncStatus.Idle); if (e instanceof UserDataSyncStoreError && e.code === UserDataSyncStoreErrorCode.Rejected) { @@ -114,7 +172,7 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser } async hasRemote(): Promise { - const remoteUserData = await this.userDataSyncStoreService.read(ExtensionsSynchroniser.EXTERNAL_USER_DATA_EXTENSIONS_KEY, null); + const remoteUserData = await this.getRemoteUserData(); return remoteUserData.content !== null; } @@ -134,13 +192,13 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser }); } - private async doSync(): Promise { + private async getPreview(): Promise { const lastSyncData = await this.getLastSyncUserData(); const lastSyncExtensions: ISyncExtension[] | null = lastSyncData ? JSON.parse(lastSyncData.content!) : null; - let skippedExtensions: ISyncExtension[] = lastSyncData ? lastSyncData.skippedExtensions || [] : []; + const skippedExtensions: ISyncExtension[] = lastSyncData ? lastSyncData.skippedExtensions || [] : []; - let remoteData = await this.userDataSyncStoreService.read(ExtensionsSynchroniser.EXTERNAL_USER_DATA_EXTENSIONS_KEY, lastSyncData); - const remoteExtensions: ISyncExtension[] = remoteData.content ? JSON.parse(remoteData.content) : null; + const remoteUserData = await this.getRemoteUserData(lastSyncData); + const remoteExtensions: ISyncExtension[] = remoteUserData.content ? JSON.parse(remoteUserData.content) : null; const localExtensions = await this.getLocalExtensions(); @@ -150,9 +208,16 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser this.logService.info('Extensions: Remote extensions does not exist. Synchronizing extensions for the first time.'); } - const ignoredExtensions = this.configurationService.getValue('sync.ignoredExtensions') || []; - const { added, removed, updated, remote } = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, ignoredExtensions); + const { added, removed, updated, remote } = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, this.getIgnoredExtensions()); + return { added, removed, updated, remote, skippedExtensions, remoteUserData }; + } + + private getIgnoredExtensions() { + return this.configurationService.getValue('sync.ignoredExtensions') || []; + } + + private async apply({ added, removed, updated, remote, remoteUserData, skippedExtensions }: ISyncPreviewResult): Promise { if (!added.length && !removed.length && !updated.length && !remote) { this.logService.trace('Extensions: No changes found during synchronizing extensions.'); } @@ -165,15 +230,13 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser if (remote) { // update remote this.logService.info('Extensions: Updating remote extensions...'); - remoteData = await this.writeToRemote(remote, remoteData.ref); + remoteUserData = await this.writeToRemote(remote, remoteUserData ? remoteUserData.ref : null); } - if (remoteData.content - && (!lastSyncData || lastSyncData.ref !== remoteData.ref) - ) { + if (remoteUserData?.content) { // update last sync this.logService.info('Extensions: Updating last synchronised extensions...'); - await this.updateLastSyncValue({ ...remoteData, skippedExtensions }); + await this.updateLastSyncValue({ ...remoteUserData, skippedExtensions }); } } @@ -243,6 +306,10 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser await this.fileService.writeFile(this.lastSyncExtensionsResource, VSBuffer.fromString(JSON.stringify(lastSyncUserData))); } + private getRemoteUserData(lastSyncData?: IUserData | null): Promise { + return this.userDataSyncStoreService.read(ExtensionsSynchroniser.EXTERNAL_USER_DATA_EXTENSIONS_KEY, lastSyncData || null); + } + private async writeToRemote(extensions: ISyncExtension[], ref: string | null): Promise { const content = JSON.stringify(extensions); ref = await this.userDataSyncStoreService.write(ExtensionsSynchroniser.EXTERNAL_USER_DATA_EXTENSIONS_KEY, content, ref); diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 5677baaf1f1..36c81f2e038 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -19,6 +19,12 @@ import { parse } from 'vs/base/common/json'; const argvProperties: string[] = ['locale']; +interface ISyncPreviewResult { + readonly local: IGlobalState | undefined; + readonly remote: IGlobalState | undefined; + readonly remoteUserData: IUserData | null; +} + export class GlobalStateSynchroniser extends Disposable implements ISynchroniser { private static EXTERNAL_USER_DATA_GLOBAL_STATE_KEY: string = 'globalState'; @@ -53,6 +59,58 @@ export class GlobalStateSynchroniser extends Disposable implements ISynchroniser } } + async pull(): Promise { + if (!this.configurationService.getValue('sync.enableUIState')) { + this.logService.info('UI State: Skipped pulling ui state as it is disabled.'); + return; + } + + this.stop(); + + try { + this.logService.info('UI State: Started pulling ui state...'); + this.setStatus(SyncStatus.Syncing); + + const remoteUserData = await this.getRemoteUserData(); + + if (remoteUserData.content !== null) { + const local: IGlobalState = JSON.parse(remoteUserData.content); + await this.apply({ local, remote: undefined, remoteUserData }); + } + + // No remote exists to pull + else { + this.logService.info('UI State: Remote UI state does not exist.'); + } + + this.logService.info('UI State: Finished pulling UI state.'); + } finally { + this.setStatus(SyncStatus.Idle); + } + } + + async push(): Promise { + if (!this.configurationService.getValue('sync.enableUIState')) { + this.logService.info('UI State: Skipped pushing UI State as it is disabled.'); + return; + } + + this.stop(); + + try { + this.logService.info('UI State: Started pushing UI State...'); + this.setStatus(SyncStatus.Syncing); + + const remote = await this.getLocalGlobalState(); + await this.apply({ local: undefined, remote, remoteUserData: null }); + + this.logService.info('UI State: Finished pulling UI State.'); + } finally { + this.setStatus(SyncStatus.Idle); + } + + } + async sync(): Promise { if (!this.configurationService.getValue('sync.enableUIState')) { this.logService.trace('UI State: Skipping synchronizing UI state as it is disabled.'); @@ -68,9 +126,9 @@ export class GlobalStateSynchroniser extends Disposable implements ISynchroniser this.setStatus(SyncStatus.Syncing); try { - await this.doSync(); + const result = await this.getPreview(); + await this.apply(result); this.logService.trace('UI State: Finised synchronizing ui state.'); - this.setStatus(SyncStatus.Idle); return true; } catch (e) { this.setStatus(SyncStatus.Idle); @@ -80,6 +138,8 @@ export class GlobalStateSynchroniser extends Disposable implements ISynchroniser return this.sync(); } throw e; + } finally { + this.setStatus(SyncStatus.Idle); } } @@ -91,21 +151,25 @@ export class GlobalStateSynchroniser extends Disposable implements ISynchroniser } async hasRemote(): Promise { - const remoteUserData = await this.userDataSyncStoreService.read(GlobalStateSynchroniser.EXTERNAL_USER_DATA_GLOBAL_STATE_KEY, null); + const remoteUserData = await this.getRemoteUserData(); return remoteUserData.content !== null; } - private async doSync(): Promise { + private async getPreview(): Promise { const lastSyncData = await this.getLastSyncUserData(); const lastSyncGlobalState = lastSyncData && lastSyncData.content ? JSON.parse(lastSyncData.content) : null; - let remoteData = await this.userDataSyncStoreService.read(GlobalStateSynchroniser.EXTERNAL_USER_DATA_GLOBAL_STATE_KEY, lastSyncData); - const remoteGlobalState: IGlobalState = remoteData.content ? JSON.parse(remoteData.content) : null; + const remoteUserData = await this.getRemoteUserData(); + const remoteGlobalState: IGlobalState = remoteUserData.content ? JSON.parse(remoteUserData.content) : null; const localGloablState = await this.getLocalGlobalState(); const { local, remote } = merge(localGloablState, remoteGlobalState, lastSyncGlobalState); + return { local, remote, remoteUserData }; + } + + private async apply({ local, remote, remoteUserData }: ISyncPreviewResult): Promise { if (local) { // update local this.logService.info('UI State: Updating local ui state...'); @@ -115,15 +179,13 @@ export class GlobalStateSynchroniser extends Disposable implements ISynchroniser if (remote) { // update remote this.logService.info('UI State: Updating remote ui state...'); - remoteData = await this.writeToRemote(remote, remoteData.ref); + remoteUserData = await this.writeToRemote(remote, remoteUserData ? remoteUserData.ref : null); } - if (remoteData.content - && (!lastSyncData || lastSyncData.ref !== remoteData.ref) - ) { + if (remoteUserData?.content) { // update last sync this.logService.info('UI State: Updating last synchronised ui state...'); - await this.updateLastSyncValue(remoteData); + await this.updateLastSyncValue(remoteUserData); } } @@ -166,6 +228,10 @@ export class GlobalStateSynchroniser extends Disposable implements ISynchroniser await this.fileService.writeFile(this.lastSyncGlobalStateResource, VSBuffer.fromString(JSON.stringify(remoteUserData))); } + private getRemoteUserData(lastSyncData?: IUserData | null): Promise { + return this.userDataSyncStoreService.read(GlobalStateSynchroniser.EXTERNAL_USER_DATA_GLOBAL_STATE_KEY, lastSyncData || null); + } + private async writeToRemote(globalState: IGlobalState, ref: string | null): Promise { const content = JSON.stringify(globalState); ref = await this.userDataSyncStoreService.write(GlobalStateSynchroniser.EXTERNAL_USER_DATA_GLOBAL_STATE_KEY, content, ref); diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 1b92064f972..bea6e5671c8 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -30,7 +30,7 @@ interface ISyncContent { interface ISyncPreviewResult { readonly fileContent: IFileContent | null; - readonly remoteUserData: IUserData; + readonly remoteUserData: IUserData | null; readonly hasLocalChanged: boolean; readonly hasRemoteChanged: boolean; readonly hasConflicts: boolean; @@ -73,6 +73,79 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser } } + async pull(): Promise { + if (!this.configurationService.getValue('sync.enableKeybindings')) { + this.logService.info('Keybindings: Skipped pulling keybindings as it is disabled.'); + return; + } + + this.stop(); + + try { + this.logService.info('Keybindings: Started pulling keybindings...'); + this.setStatus(SyncStatus.Syncing); + + const remoteUserData = await this.getRemoteUserData(); + const remoteContent = remoteUserData.content !== null ? this.getKeybindingsContentFromSyncContent(remoteUserData.content) : null; + + if (remoteContent !== null) { + await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(remoteContent)); + this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve({ + fileContent: null, + hasConflicts: false, + hasLocalChanged: true, + hasRemoteChanged: false, + remoteUserData + })); + await this.apply(); + } + + // No remote exists to pull + else { + this.logService.info('Keybindings: Remote keybindings does not exist.'); + } + } finally { + this.setStatus(SyncStatus.Idle); + } + + } + + async push(): Promise { + if (!this.configurationService.getValue('sync.enableKeybindings')) { + this.logService.info('Keybindings: Skipped pushing keybindings as it is disabled.'); + return; + } + + this.stop(); + + try { + this.logService.info('Keybindings: Started pushing keybindings...'); + this.setStatus(SyncStatus.Syncing); + + const fileContent = await this.getLocalFileContent(); + + if (fileContent !== null) { + await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, fileContent.value); + this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve({ + fileContent, + hasConflicts: false, + hasLocalChanged: false, + hasRemoteChanged: true, + remoteUserData: null + })); + await this.apply(); + } + + // No local exists to push + else { + this.logService.info('Keybindings: Local keybindings does not exist.'); + } + } finally { + this.setStatus(SyncStatus.Idle); + } + + } + async sync(_continue?: boolean): Promise { if (!this.configurationService.getValue('sync.enableKeybindings')) { this.logService.trace('Keybindings: Skipping synchronizing keybindings as it is disabled.'); @@ -115,6 +188,8 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser return this.sync(); } throw e; + } finally { + this.setStatus(SyncStatus.Idle); } } @@ -134,7 +209,7 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser } async hasRemote(): Promise { - const remoteUserData = await this.userDataSyncStoreService.read(KeybindingsSynchroniser.EXTERNAL_USER_DATA_KEYBINDINGS_KEY, null); + const remoteUserData = await this.getRemoteUserData(); return remoteUserData.content !== null; } @@ -143,6 +218,7 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser return false; } await this.apply(); + this.setStatus(SyncStatus.Idle); return true; } @@ -170,11 +246,12 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser } if (hasRemoteChanged) { this.logService.info('Keybindings: Updating remote keybindings'); - const remoteContents = this.updateSyncContent(content, remoteUserData.content); - const ref = await this.updateRemoteUserData(remoteContents, remoteUserData.ref); + let remoteContents = remoteUserData ? remoteUserData.content : (await this.getRemoteUserData()).content; + remoteContents = this.updateSyncContent(content, remoteContents); + const ref = await this.updateRemoteUserData(remoteContents, remoteUserData ? remoteUserData.ref : null); remoteUserData = { ref, content: remoteContents }; } - if (remoteUserData.content) { + if (remoteUserData?.content) { this.logService.info('Keybindings: Updating last synchronised keybindings'); const lastSyncContent = this.updateSyncContent(content, null); await this.updateLastSyncUserData({ ref: remoteUserData.ref, content: lastSyncContent }); @@ -188,7 +265,6 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser this.logService.trace('Keybindings: Finised synchronizing keybindings.'); this.syncPreviewResultPromise = null; - this.setStatus(SyncStatus.Idle); } private hasErrors(content: string): boolean { @@ -210,7 +286,7 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser const remoteUserData = await this.getRemoteUserData(lastSyncData); const remoteContent = remoteUserData.content ? this.getKeybindingsContentFromSyncContent(remoteUserData.content) : null; // Get file content last to get the latest - const fileContent = await this.getLocalContent(); + const fileContent = await this.getLocalFileContent(); let hasLocalChanged: boolean = false; let hasRemoteChanged: boolean = false; let hasConflicts: boolean = false; @@ -262,7 +338,7 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser return this._formattingOptions; } - private async getLocalContent(): Promise { + private async getLocalFileContent(): Promise { try { return await this.fileService.readFile(this.environmentService.keybindingsResource); } catch (error) { @@ -293,8 +369,8 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser await this.fileService.writeFile(this.lastSyncKeybindingsResource, VSBuffer.fromString(JSON.stringify(remoteUserData))); } - private async getRemoteUserData(lastSyncData: IUserData | null): Promise { - return this.userDataSyncStoreService.read(KeybindingsSynchroniser.EXTERNAL_USER_DATA_KEYBINDINGS_KEY, lastSyncData); + private async getRemoteUserData(lastSyncData?: IUserData | null): Promise { + return this.userDataSyncStoreService.read(KeybindingsSynchroniser.EXTERNAL_USER_DATA_KEYBINDINGS_KEY, lastSyncData || null); } private async updateRemoteUserData(content: string, ref: string | null): Promise { diff --git a/src/vs/platform/userDataSync/common/settingsMerge.ts b/src/vs/platform/userDataSync/common/settingsMerge.ts index 1b710e133bd..c00080dbcb1 100644 --- a/src/vs/platform/userDataSync/common/settingsMerge.ts +++ b/src/vs/platform/userDataSync/common/settingsMerge.ts @@ -12,17 +12,14 @@ import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import * as contentUtil from 'vs/platform/userDataSync/common/content'; import { IConflictSetting } from 'vs/platform/userDataSync/common/userDataSync'; -export function computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: string[], formattingOptions: FormattingOptions): string { +export function updateIgnoredSettings(targetContent: string, sourceContent: string, ignoredSettings: string[], formattingOptions: FormattingOptions): string { if (ignoredSettings.length) { - const remote = parse(remoteContent); - const ignored = ignoredSettings.reduce((set, key) => { set.add(key); return set; }, new Set()); + const source = parse(sourceContent); for (const key of ignoredSettings) { - if (ignored.has(key)) { - localContent = contentUtil.edit(localContent, [key], remote[key], formattingOptions); - } + targetContent = contentUtil.edit(targetContent, [key], source[key], formattingOptions); } } - return localContent; + return targetContent; } export function merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: string[], resolvedConflicts: { key: string, value: any | undefined }[], formattingOptions: FormattingOptions): { mergeContent: string, hasChanges: boolean, conflicts: IConflictSetting[] } { diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 89dc8c0ae81..813826aa728 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -17,14 +17,14 @@ import { joinPath, dirname } from 'vs/base/common/resources'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { startsWith } from 'vs/base/common/strings'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { computeRemoteContent, merge } from 'vs/platform/userDataSync/common/settingsMerge'; +import { updateIgnoredSettings, merge } from 'vs/platform/userDataSync/common/settingsMerge'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import * as arrays from 'vs/base/common/arrays'; import * as objects from 'vs/base/common/objects'; interface ISyncPreviewResult { readonly fileContent: IFileContent | null; - readonly remoteUserData: IUserData; + readonly remoteUserData: IUserData | null; readonly hasLocalChanged: boolean; readonly hasRemoteChanged: boolean; readonly conflicts: IConflictSetting[]; @@ -86,6 +86,87 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer } } + async pull(): Promise { + if (!this.configurationService.getValue('sync.enableSettings')) { + this.logService.info('Settings: Skipped pulling settings as it is disabled.'); + return; + } + + this.stop(); + + try { + this.logService.info('Settings: Started pulling settings...'); + this.setStatus(SyncStatus.Syncing); + + const remoteUserData = await this.getRemoteUserData(); + + if (remoteUserData.content !== null) { + const fileContent = await this.getLocalFileContent(); + const formatUtils = await this.getFormattingOptions(); + // Update ignored settings + const content = updateIgnoredSettings(remoteUserData.content, fileContent ? fileContent.value.toString() : '{}', getIgnoredSettings(this.configurationService), formatUtils); + await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(content)); + + this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve({ + conflicts: [], + fileContent, + hasLocalChanged: true, + hasRemoteChanged: false, + remoteUserData + })); + + await this.apply(); + } + + // No remote exists to pull + else { + this.logService.info('Settings: Remote settings does not exist.'); + } + } finally { + this.setStatus(SyncStatus.Idle); + } + } + + async push(): Promise { + if (!this.configurationService.getValue('sync.enableSettings')) { + this.logService.info('Settings: Skipped pushing settings as it is disabled.'); + return; + } + + this.stop(); + + try { + this.logService.info('Settings: Started pushing settings...'); + this.setStatus(SyncStatus.Syncing); + + const fileContent = await this.getLocalFileContent(); + + if (fileContent !== null) { + const formatUtils = await this.getFormattingOptions(); + // Remove ignored settings + const content = updateIgnoredSettings(fileContent.value.toString(), '{}', getIgnoredSettings(this.configurationService), formatUtils); + await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(content)); + + this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve({ + conflicts: [], + fileContent, + hasLocalChanged: false, + hasRemoteChanged: true, + remoteUserData: null + })); + + await this.apply(); + } + + // No local exists to push + else { + this.logService.info('Settings: Local settings does not exist.'); + } + } finally { + this.setStatus(SyncStatus.Idle); + } + } + async sync(_continue?: boolean): Promise { if (!this.configurationService.getValue('sync.enableSettings')) { this.logService.trace('Settings: Skipping synchronizing settings as it is disabled.'); @@ -123,7 +204,7 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer } async hasRemote(): Promise { - const remoteUserData = await this.userDataSyncStoreService.read(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY, null); + const remoteUserData = await this.getRemoteUserData(); return remoteUserData.content !== null; } @@ -159,12 +240,15 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer return this.sync(); } throw e; + } finally { + this.setStatus(SyncStatus.Idle); } } private async continueSync(): Promise { if (this.status === SyncStatus.HasConflicts) { await this.apply(); + this.setStatus(SyncStatus.Idle); } return true; } @@ -193,12 +277,12 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer } if (hasRemoteChanged) { const formatUtils = await this.getFormattingOptions(); - const remoteContent = remoteUserData.content ? computeRemoteContent(content, remoteUserData.content, getIgnoredSettings(this.configurationService, content), formatUtils) : content; + const remoteContent = remoteUserData?.content ? updateIgnoredSettings(content, remoteUserData.content, getIgnoredSettings(this.configurationService, content), formatUtils) : content; this.logService.info('Settings: Updating remote settings'); - const ref = await this.writeToRemote(remoteContent, remoteUserData.ref); + const ref = await this.writeToRemote(remoteContent, remoteUserData ? remoteUserData.ref : null); remoteUserData = { ref, content }; } - if (remoteUserData.content) { + if (remoteUserData?.content) { this.logService.info('Settings: Updating last synchronised sttings'); await this.updateLastSyncValue(remoteUserData); } @@ -211,7 +295,6 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer this.logService.trace('Settings: Finised synchronizing settings.'); this.syncPreviewResultPromise = null; - this.setStatus(SyncStatus.Idle); } private hasErrors(content: string): boolean { @@ -229,7 +312,7 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer private async generatePreview(resolvedConflicts: { key: string, value: any }[], token: CancellationToken): Promise { const lastSyncData = await this.getLastSyncUserData(); - const remoteUserData = await this.userDataSyncStoreService.read(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY, lastSyncData); + const remoteUserData = await this.getRemoteUserData(lastSyncData); const remoteContent: string | null = remoteUserData.content; // Get file content last to get the latest const fileContent = await this.getLocalFileContent(); @@ -303,6 +386,10 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer } } + private getRemoteUserData(lastSyncData?: IUserData | null): Promise { + return this.userDataSyncStoreService.read(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY, lastSyncData || null); + } + private async writeToRemote(content: string, ref: string | null): Promise { return this.userDataSyncStoreService.write(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY, content, ref); } diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 3558afcb8e8..eacd383b650 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -184,6 +184,8 @@ export interface ISynchroniser { readonly status: SyncStatus; readonly onDidChangeStatus: Event; readonly onDidChangeLocal: Event; + pull(): Promise; + push(): Promise; sync(_continue?: boolean): Promise; stop(): void; hasPreviouslySynced(): Promise diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index 05f4d99128f..61f634471ba 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -25,6 +25,8 @@ export class UserDataSyncChannel implements IServerChannel { call(context: any, command: string, args?: any): Promise { switch (command) { case 'sync': return this.service.sync(args[0]); + case 'pull': return this.service.pull(); + case 'push': return this.service.push(); case '_getInitialStatus': return Promise.resolve(this.service.status); case 'getConflictsSource': return Promise.resolve(this.service.conflictsSource); case 'removeExtension': return this.service.removeExtension(args[0]); @@ -52,6 +54,8 @@ export class SettingsSyncChannel implements IServerChannel { call(context: any, command: string, args?: any): Promise { switch (command) { case 'sync': return this.service.sync(args[0]); + case 'pull': return this.service.pull(); + case 'push': return this.service.push(); case '_getInitialStatus': return Promise.resolve(this.service.status); case '_getInitialConflicts': return Promise.resolve(this.service.conflicts); case 'stop': this.service.stop(); return Promise.resolve(); diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 5a23f9d01c4..9c4aaf3cb0c 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -56,6 +56,38 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal)); } + async pull(): Promise { + if (!this.userDataSyncStoreService.userDataSyncStore) { + throw new Error('Not enabled'); + } + if (this.authTokenService.status === AuthTokenStatus.SignedOut) { + throw new Error('Not Authenticated. Please sign in to start sync.'); + } + for (const synchroniser of this.synchronisers) { + try { + await synchroniser.pull(); + } catch (e) { + this.logService.error(`${this.getSyncSource(synchroniser)}: ${toErrorMessage(e)}`); + } + } + } + + async push(): Promise { + if (!this.userDataSyncStoreService.userDataSyncStore) { + throw new Error('Not enabled'); + } + if (this.authTokenService.status === AuthTokenStatus.SignedOut) { + throw new Error('Not Authenticated. Please sign in to start sync.'); + } + for (const synchroniser of this.synchronisers) { + try { + await synchroniser.push(); + } catch (e) { + this.logService.error(`${this.getSyncSource(synchroniser)}: ${toErrorMessage(e)}`); + } + } + } + async sync(_continue?: boolean): Promise { if (!this.userDataSyncStoreService.userDataSyncStore) { throw new Error('Not enabled'); diff --git a/src/vs/platform/userDataSync/test/common/settingsMerge.test.ts b/src/vs/platform/userDataSync/test/common/settingsMerge.test.ts index 343c99b38d6..4f51f87c2e3 100644 --- a/src/vs/platform/userDataSync/test/common/settingsMerge.test.ts +++ b/src/vs/platform/userDataSync/test/common/settingsMerge.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { merge, computeRemoteContent } from 'vs/platform/userDataSync/common/settingsMerge'; +import { merge, updateIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { IConflictSetting } from 'vs/platform/userDataSync/common/userDataSync'; const formattingOptions = { eol: '\n', insertSpaces: false, tabSize: 4 }; @@ -567,7 +567,7 @@ suite('SettingsMerge - Compute Remote Content', () => { 'd': 4, 'e': 6, }); - const actual = computeRemoteContent(localContent, remoteContent, [], formattingOptions); + const actual = updateIgnoredSettings(localContent, remoteContent, [], formattingOptions); assert.equal(actual, localContent); }); @@ -588,7 +588,7 @@ suite('SettingsMerge - Compute Remote Content', () => { 'b': 2, 'c': 3, }); - const actual = computeRemoteContent(localContent, remoteContent, ['a'], formattingOptions); + const actual = updateIgnoredSettings(localContent, remoteContent, ['a'], formattingOptions); assert.equal(actual, expected); }); diff --git a/src/vs/workbench/services/userDataSync/electron-browser/settingsSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/settingsSyncService.ts index 51c3a999ff6..345482f53b1 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/settingsSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/settingsSyncService.ts @@ -45,6 +45,14 @@ export class SettingsSyncService extends Disposable implements ISettingsSyncServ }); } + pull(): Promise { + return this.channel.call('pull'); + } + + push(): Promise { + return this.channel.call('push'); + } + sync(_continue?: boolean): Promise { return this.channel.call('sync', [_continue]); } diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts index fe09e70eec6..a8b04ea9302 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts @@ -38,6 +38,14 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ }); } + pull(): Promise { + return this.channel.call('pull'); + } + + push(): Promise { + return this.channel.call('push'); + } + sync(_continue?: boolean): Promise { return this.channel.call('sync', [_continue]); } From 79a01a0f27bd9f21bee8e278b56bf51bbc9dd962 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Tue, 14 Jan 2020 17:20:41 +0100 Subject: [PATCH 277/843] Revert isDetailsResolved and implement CompletionItemLabel --- .../client/src/cssMain.ts | 11 +++++--- src/vs/editor/common/modes.ts | 25 ++++++++++++++++-- .../editor/contrib/suggest/completionModel.ts | 7 ++--- src/vs/editor/contrib/suggest/suggest.ts | 17 +++++++----- .../contrib/suggest/suggestController.ts | 3 ++- .../editor/contrib/suggest/suggestWidget.ts | 26 +++++++++++++------ src/vs/editor/contrib/suggest/wordDistance.ts | 5 +++- src/vs/monaco.d.ts | 21 +++++++++++++-- src/vs/vscode.d.ts | 2 +- src/vs/vscode.proposed.d.ts | 22 ++++++++++++++-- .../api/browser/mainThreadLanguageFeatures.ts | 7 +++-- .../workbench/api/common/extHost.protocol.ts | 3 +-- .../api/common/extHostLanguageFeatures.ts | 9 ++++--- src/vs/workbench/api/common/extHostTypes.ts | 12 ++++++--- .../browser/snippetCompletionProvider.ts | 2 +- 15 files changed, 131 insertions(+), 41 deletions(-) diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index bc8bbdc9ff7..8822571d076 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -60,12 +60,17 @@ export function activate(context: ExtensionContext) { (Array.isArray(r) ? r : r.items).forEach(updateRanges); if (!Array.isArray(r)) { - r.isDetailsResolved = true; r.items.forEach(i => { if (i.kind === CompletionItemKind.Color) { - i.detail = i.documentation?.toString(); + i.label = { + label: i.label as string, + details: i.documentation?.toString() + }; } else { - i.detail = i.label; + i.label = { + label: i.label as string, + details: i.label as string + }; } }); } diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 78495217dc3..f22125a6acb 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -369,6 +369,28 @@ export let completionKindFromString: { }; })(); +export interface CompletionItemLabel { + + /** + * The label of this completion item. By default + * this is also the text that is inserted when selecting + * this completion. + */ + label: string; + + /** + * A description of the completion item which is rendered + * less prominent. + */ + // description?: string; + + /** + * Details of the completion item that is rendered less + * prominent to the right. + */ + details?: string; +} + export const enum CompletionItemTag { Deprecated = 1 } @@ -396,7 +418,7 @@ export interface CompletionItem { * this is also the text that is inserted when selecting * this completion. */ - label: string; + label: string | CompletionItemLabel; /** * The kind of this completion item. Based on the kind * an icon is chosen by the editor. @@ -481,7 +503,6 @@ export interface CompletionItem { export interface CompletionList { suggestions: CompletionItem[]; incomplete?: boolean; - isDetailsResolved?: boolean; dispose?(): void; } diff --git a/src/vs/editor/contrib/suggest/completionModel.ts b/src/vs/editor/contrib/suggest/completionModel.ts index c1349fa7935..5382be986e9 100644 --- a/src/vs/editor/contrib/suggest/completionModel.ts +++ b/src/vs/editor/contrib/suggest/completionModel.ts @@ -192,6 +192,7 @@ export class CompletionModel { } } + const label = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.label; if (wordPos >= wordLen) { // the wordPos at which scoring starts is the whole word // and therefore the same rules as not having a word apply @@ -206,19 +207,19 @@ export class CompletionModel { if (!match) { continue; // NO match } - if (compareIgnoreCase(item.completion.filterText, item.completion.label) === 0) { + if (compareIgnoreCase(item.completion.filterText, label) === 0) { // filterText and label are actually the same -> use good highlights item.score = match; } else { // re-run the scorer on the label in the hope of a result BUT use the rank // of the filterText-match - item.score = anyScore(word, wordLow, wordPos, item.completion.label, item.labelLow, 0); + item.score = anyScore(word, wordLow, wordPos, label, item.labelLow, 0); item.score[0] = match[0]; // use score from filterText } } else { // by default match `word` against the `label` - let match = scoreFn(word, wordLow, wordPos, item.completion.label, item.labelLow, 0, false); + let match = scoreFn(word, wordLow, wordPos, label, item.labelLow, 0, false); if (!match) { continue; // NO match } diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index f408f88ec12..61813788b10 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -36,6 +36,9 @@ export class CompletionItem { readonly editInsertEnd: IPosition; readonly editReplaceEnd: IPosition; + // + readonly textLabel: string; + // perf readonly labelLow: string; readonly sortTextLow?: string; @@ -47,9 +50,6 @@ export class CompletionItem { idx?: number; word?: string; - // all details resolved, we can show them all - readonly isDetailsResolved: boolean; - constructor( readonly position: IPosition, readonly completion: modes.CompletionItem, @@ -57,8 +57,13 @@ export class CompletionItem { readonly provider: modes.CompletionItemProvider, model: ITextModel ) { + this.textLabel = typeof completion.label === 'string' + ? completion.label + : completion.label.label; + // ensure lower-variants (perf) - this.labelLow = completion.label.toLowerCase(); + this.labelLow = this.textLabel.toLowerCase(); + this.sortTextLow = completion.sortText && completion.sortText.toLowerCase(); this.filterTextLow = completion.filterText && completion.filterText.toLowerCase(); @@ -73,8 +78,6 @@ export class CompletionItem { this.editReplaceEnd = new Position(completion.range.replace.endLineNumber, completion.range.replace.endColumn); } - this.isDetailsResolved = container.isDetailsResolved || typeof provider.resolveCompletionItem === 'undefined'; - // create the suggestion resolver const { resolveCompletionItem } = provider; if (typeof resolveCompletionItem !== 'function') { @@ -188,7 +191,7 @@ export function provideSuggestionItems( } // fill in default sortText when missing if (!suggestion.sortText) { - suggestion.sortText = suggestion.label; + suggestion.sortText = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.label; } allSuggestions.push(new CompletionItem(position, suggestion, container, provider, model)); diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index a4330d1d438..b0fa140844a 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -343,8 +343,9 @@ export class SuggestController implements IEditorContribution { } private _alertCompletionItem({ completion: suggestion }: CompletionItem): void { + const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.label; if (isNonEmptyArray(suggestion.additionalTextEdits)) { - let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' made {1} additional edits", suggestion.label, suggestion.additionalTextEdits.length); + let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' made {1} additional edits", textLabel, suggestion.additionalTextEdits.length); alert(msg); } } diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 24bb13a3043..90f1a7ba65d 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -67,8 +67,12 @@ export const editorSuggestWidgetHighlightForeground = registerColor('editorSugge const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i; function extractColor(item: CompletionItem, out: string[]): boolean { - if (item.completion.label.match(colorRegExp)) { - out[0] = item.completion.label; + const label = typeof item.completion.label === 'string' + ? item.completion.label + : item.completion.label.label; + + if (label.match(colorRegExp)) { + out[0] = label; return true; } if (typeof item.completion.documentation === 'string' && item.completion.documentation.match(colorRegExp)) { @@ -163,6 +167,7 @@ class Renderer implements IListRenderer renderElement(element: CompletionItem, _index: number, templateData: ISuggestionTemplateData): void { const data = templateData; const suggestion = (element).completion; + const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.label; data.icon.className = 'icon ' + completionKindToCssClass(suggestion.kind); data.colorspan.style.backgroundColor = ''; @@ -183,7 +188,7 @@ class Renderer implements IListRenderer // special logic for 'file' completion items data.icon.className = 'icon hide'; data.iconContainer.className = 'icon hide'; - const labelClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FILE); + const labelClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: textLabel }), FileKind.FILE); const detailClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FILE); labelOptions.extraClasses = labelClasses.length > detailClasses.length ? labelClasses : detailClasses; @@ -192,7 +197,7 @@ class Renderer implements IListRenderer data.icon.className = 'icon hide'; data.iconContainer.className = 'icon hide'; labelOptions.extraClasses = flatten([ - getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FOLDER), + getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: textLabel }), FileKind.FOLDER), getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FOLDER) ]); } else { @@ -207,8 +212,12 @@ class Renderer implements IListRenderer labelOptions.matches = []; } - data.iconLabel.setLabel(suggestion.label, undefined, labelOptions); - data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); + data.iconLabel.setLabel(textLabel, undefined, labelOptions); + if (typeof suggestion.label === 'string') { + data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); + } else { + data.typeLabel.textContent = (suggestion.label.details || '').replace(/\n.*$/m, ''); + } if (canExpandCompletionItem(element)) { show(data.readMore); @@ -621,10 +630,11 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate MainThreadLanguageFeatures._inflateSuggestDto(result.a, d)), incomplete: result.c, - isDetailsResolved: result.d, dispose: () => typeof result.x === 'number' && this._proxy.$releaseCompletionItems(handle, result.x) }; }); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 227a885f876..5b65014b083 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -989,7 +989,7 @@ export const enum ISuggestDataDtoField { } export interface ISuggestDataDto { - [ISuggestDataDtoField.label]: string; + [ISuggestDataDtoField.label]: string | modes.CompletionItemLabel; [ISuggestDataDtoField.kind]: modes.CompletionItemKind; [ISuggestDataDtoField.detail]?: string; [ISuggestDataDtoField.documentation]?: string | IMarkdownString; @@ -1012,7 +1012,6 @@ export interface ISuggestResultDto { a: { insert: IRange, replace: IRange; }; b: ISuggestDataDto[]; c?: boolean; - d?: boolean; } export interface ISignatureHelpDto { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 737ed4be714..19fa91f1de3 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -787,8 +787,7 @@ class SuggestAdapter { x: pid, b: [], a: { replace: typeConvert.Range.from(replaceRange), insert: typeConvert.Range.from(insertRange) }, - c: list.isIncomplete || undefined, - d: list.isDetailsResolved || undefined + c: list.isIncomplete || undefined }; for (let i = 0; i < list.items.length; i++) { @@ -858,7 +857,11 @@ class SuggestAdapter { } private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: extHostProtocol.ChainedCacheId): extHostProtocol.ISuggestDataDto | undefined { - if (typeof item.label !== 'string' || item.label.length === 0) { + const label = typeof item.label === 'string' + ? item.label + : item.label.label; + + if (typeof label !== 'string' || label.length === 0) { this._logService.warn('INVALID text edit -> must have at least a label'); return undefined; } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 5239da9499b..19863726fac 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1351,10 +1351,17 @@ export enum CompletionItemTag { Deprecated = 1, } +export interface CompletionItemLabel { + label: string; + // description?: string; + details?: string; +} + + @es5ClassCompat export class CompletionItem implements vscode.CompletionItem { - label: string; + label: string | CompletionItemLabel; kind?: CompletionItemKind; tags?: CompletionItemTag[]; detail?: string; @@ -1371,7 +1378,7 @@ export class CompletionItem implements vscode.CompletionItem { additionalTextEdits?: TextEdit[]; command?: vscode.Command; - constructor(label: string, kind?: CompletionItemKind) { + constructor(label: string | CompletionItemLabel, kind?: CompletionItemKind) { this.label = label; this.kind = kind; } @@ -1395,7 +1402,6 @@ export class CompletionItem implements vscode.CompletionItem { export class CompletionList { isIncomplete?: boolean; - isDetailsResolved?: boolean; items: vscode.CompletionItem[]; constructor(items: vscode.CompletionItem[] = [], isIncomplete: boolean = false) { diff --git a/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts b/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts index e30853b4c2c..96d9f026104 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts @@ -146,7 +146,7 @@ export class SnippetCompletionProvider implements CompletionItemProvider { i = to; } } - return { suggestions, isDetailsResolved: true }; + return { suggestions }; }); } From d5c8eac689b6a075a8203a21d2d27a156288e4b5 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 14 Jan 2020 17:33:26 +0100 Subject: [PATCH 278/843] :lipstick: --- .../editor/browser/controller/mouseHandler.ts | 40 +++++++++---------- .../editor/browser/widget/inlineDiffMargin.ts | 10 ++--- .../contrib/codelens/codelensController.ts | 12 +++--- .../editor/contrib/codelens/codelensWidget.ts | 26 ++++++------ src/vs/editor/test/browser/testCodeEditor.ts | 4 +- 5 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 973a5ca1d3d..efeecf1e053 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -10,7 +10,7 @@ import { RunOnceScheduler, TimeoutTimer } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { HitTestContext, IViewZoneData, MouseTarget, MouseTargetFactory, PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ClientCoordinates, EditorMouseEvent, EditorMouseEventFactory, GlobalEditorMouseMoveMonitor, createEditorPagePosition } from 'vs/editor/browser/editorDom'; import { ViewController } from 'vs/editor/browser/view/viewController'; import { EditorZoom } from 'vs/editor/common/config/editorZoom'; @@ -148,7 +148,7 @@ export class MouseHandler extends ViewEventHandler { } // --- end event handlers - public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget | null { + public getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget | null { const clientPos = new ClientCoordinates(clientX, clientY); const pos = clientPos.toPageCoordinates(); const editorPos = createEditorPagePosition(this.viewHelper.viewDomNode); @@ -160,7 +160,7 @@ export class MouseHandler extends ViewEventHandler { return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), editorPos, pos, null); } - protected _createMouseTarget(e: EditorMouseEvent, testEventTarget: boolean): editorBrowser.IMouseTarget { + protected _createMouseTarget(e: EditorMouseEvent, testEventTarget: boolean): IMouseTarget { return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), e.editorPos, e.pos, testEventTarget ? e.target : null); } @@ -210,12 +210,12 @@ export class MouseHandler extends ViewEventHandler { public _onMouseDown(e: EditorMouseEvent): void { const t = this._createMouseTarget(e, true); - const targetIsContent = (t.type === editorBrowser.MouseTargetType.CONTENT_TEXT || t.type === editorBrowser.MouseTargetType.CONTENT_EMPTY); - const targetIsGutter = (t.type === editorBrowser.MouseTargetType.GUTTER_GLYPH_MARGIN || t.type === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS || t.type === editorBrowser.MouseTargetType.GUTTER_LINE_DECORATIONS); - const targetIsLineNumbers = (t.type === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS); + const targetIsContent = (t.type === MouseTargetType.CONTENT_TEXT || t.type === MouseTargetType.CONTENT_EMPTY); + const targetIsGutter = (t.type === MouseTargetType.GUTTER_GLYPH_MARGIN || t.type === MouseTargetType.GUTTER_LINE_NUMBERS || t.type === MouseTargetType.GUTTER_LINE_DECORATIONS); + const targetIsLineNumbers = (t.type === MouseTargetType.GUTTER_LINE_NUMBERS); const selectOnLineNumbers = this._context.configuration.options.get(EditorOption.selectOnLineNumbers); - const targetIsViewZone = (t.type === editorBrowser.MouseTargetType.CONTENT_VIEW_ZONE || t.type === editorBrowser.MouseTargetType.GUTTER_VIEW_ZONE); - const targetIsWidget = (t.type === editorBrowser.MouseTargetType.CONTENT_WIDGET); + const targetIsViewZone = (t.type === MouseTargetType.CONTENT_VIEW_ZONE || t.type === MouseTargetType.GUTTER_VIEW_ZONE); + const targetIsWidget = (t.type === MouseTargetType.CONTENT_WIDGET); let shouldHandle = e.leftButton || e.middleButton; if (platform.isMacintosh && e.leftButton && e.ctrlKey) { @@ -269,7 +269,7 @@ class MouseDownOperation extends Disposable { private readonly _context: ViewContext; private readonly _viewController: ViewController; private readonly _viewHelper: IPointerHandlerHelper; - private readonly _createMouseTarget: (e: EditorMouseEvent, testEventTarget: boolean) => editorBrowser.IMouseTarget; + private readonly _createMouseTarget: (e: EditorMouseEvent, testEventTarget: boolean) => IMouseTarget; private readonly _getMouseColumn: (e: EditorMouseEvent) => number; private readonly _mouseMoveMonitor: GlobalEditorMouseMoveMonitor; @@ -284,7 +284,7 @@ class MouseDownOperation extends Disposable { context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper, - createMouseTarget: (e: EditorMouseEvent, testEventTarget: boolean) => editorBrowser.IMouseTarget, + createMouseTarget: (e: EditorMouseEvent, testEventTarget: boolean) => IMouseTarget, getMouseColumn: (e: EditorMouseEvent) => number ) { super(); @@ -331,10 +331,10 @@ class MouseDownOperation extends Disposable { } } - public start(targetType: editorBrowser.MouseTargetType, e: EditorMouseEvent): void { + public start(targetType: MouseTargetType, e: EditorMouseEvent): void { this._lastMouseEvent = e; - this._mouseState.setStartedOnLineNumbers(targetType === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS); + this._mouseState.setStartedOnLineNumbers(targetType === MouseTargetType.GUTTER_LINE_NUMBERS); this._mouseState.setStartButtons(e); this._mouseState.setModifiers(e); const position = this._findMousePosition(e, true); @@ -356,7 +356,7 @@ class MouseDownOperation extends Disposable { && e.detail < 2 // only single click on a selection can work && !this._isActive // the mouse is not down yet && !this._currentSelection.isEmpty() // we don't drag single cursor - && (position.type === editorBrowser.MouseTargetType.CONTENT_TEXT) // single click on text + && (position.type === MouseTargetType.CONTENT_TEXT) // single click on text && position.position && this._currentSelection.containsPosition(position.position) // single click on a selection ) { this._mouseState.isDragAndDrop = true; @@ -436,12 +436,12 @@ class MouseDownOperation extends Disposable { if (viewZoneData) { const newPosition = this._helpPositionJumpOverViewZone(viewZoneData); if (newPosition) { - return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, newPosition); + return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, newPosition); } } const aboveLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset); - return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(aboveLineNumber, 1)); + return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(aboveLineNumber, 1)); } if (e.posy > editorContent.y + editorContent.height) { @@ -450,22 +450,22 @@ class MouseDownOperation extends Disposable { if (viewZoneData) { const newPosition = this._helpPositionJumpOverViewZone(viewZoneData); if (newPosition) { - return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, newPosition); + return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, newPosition); } } const belowLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset); - return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber))); + return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber))); } const possibleLineNumber = viewLayout.getLineNumberAtVerticalOffset(viewLayout.getCurrentScrollTop() + (e.posy - editorContent.y)); if (e.posx < editorContent.x) { - return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, 1)); + return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, 1)); } if (e.posx > editorContent.x + editorContent.width) { - return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, model.getLineMaxColumn(possibleLineNumber))); + return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, model.getLineMaxColumn(possibleLineNumber))); } return null; @@ -483,7 +483,7 @@ class MouseDownOperation extends Disposable { return null; } - if (t.type === editorBrowser.MouseTargetType.CONTENT_VIEW_ZONE || t.type === editorBrowser.MouseTargetType.GUTTER_VIEW_ZONE) { + if (t.type === MouseTargetType.CONTENT_VIEW_ZONE || t.type === MouseTargetType.GUTTER_VIEW_ZONE) { const newPosition = this._helpPositionJumpOverViewZone(t.detail); if (newPosition) { return new MouseTarget(t.element, t.type, t.mouseColumn, newPosition, null, t.detail); diff --git a/src/vs/editor/browser/widget/inlineDiffMargin.ts b/src/vs/editor/browser/widget/inlineDiffMargin.ts index 6ad8f6c007d..9e61bc0b05e 100644 --- a/src/vs/editor/browser/widget/inlineDiffMargin.ts +++ b/src/vs/editor/browser/widget/inlineDiffMargin.ts @@ -9,7 +9,7 @@ import { Action } from 'vs/base/common/actions'; import { Disposable } from 'vs/base/common/lifecycle'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -150,8 +150,8 @@ export class InlineDiffMargin extends Disposable { })); - this._register(editor.onMouseMove((e: editorBrowser.IEditorMouseEvent) => { - if (e.target.type === editorBrowser.MouseTargetType.CONTENT_VIEW_ZONE || e.target.type === editorBrowser.MouseTargetType.GUTTER_VIEW_ZONE) { + this._register(editor.onMouseMove((e: IEditorMouseEvent) => { + if (e.target.type === MouseTargetType.CONTENT_VIEW_ZONE || e.target.type === MouseTargetType.GUTTER_VIEW_ZONE) { const viewZoneId = e.target.detail.viewZoneId; if (viewZoneId === this._viewZoneId) { @@ -165,12 +165,12 @@ export class InlineDiffMargin extends Disposable { } })); - this._register(editor.onMouseDown((e: editorBrowser.IEditorMouseEvent) => { + this._register(editor.onMouseDown((e: IEditorMouseEvent) => { if (!e.event.rightButton) { return; } - if (e.target.type === editorBrowser.MouseTargetType.CONTENT_VIEW_ZONE || e.target.type === editorBrowser.MouseTargetType.GUTTER_VIEW_ZONE) { + if (e.target.type === MouseTargetType.CONTENT_VIEW_ZONE || e.target.type === MouseTargetType.GUTTER_VIEW_ZONE) { const viewZoneId = e.target.detail.viewZoneId; if (viewZoneId === this._viewZoneId) { diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 7a644f1bfe4..f905d33c94e 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -7,7 +7,7 @@ import { CancelablePromise, RunOnceScheduler, createCancelablePromise, disposabl import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { toDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; import { StableEditorScrollState } from 'vs/editor/browser/core/editorState'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, MouseTargetType, IViewZoneChangeAccessor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; @@ -40,7 +40,7 @@ export class CodeLensContribution implements IEditorContribution { private _detectVisibleLenses: RunOnceScheduler | undefined; constructor( - private readonly _editor: editorBrowser.ICodeEditor, + private readonly _editor: ICodeEditor, @ICommandService private readonly _commandService: ICommandService, @INotificationService private readonly _notificationService: INotificationService, @ICodeLensCache private readonly _codeLensCache: ICodeLensCache @@ -223,7 +223,7 @@ export class CodeLensContribution implements IEditorContribution { } })); this._localToDispose.add(this._editor.onMouseUp(e => { - if (e.target.type !== editorBrowser.MouseTargetType.CONTENT_WIDGET) { + if (e.target.type !== MouseTargetType.CONTENT_WIDGET) { return; } let target = e.target.element; @@ -243,7 +243,7 @@ export class CodeLensContribution implements IEditorContribution { scheduler.schedule(); } - private _disposeAllLenses(decChangeAccessor: IModelDecorationsChangeAccessor | undefined, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor | undefined): void { + private _disposeAllLenses(decChangeAccessor: IModelDecorationsChangeAccessor | undefined, viewZoneChangeAccessor: IViewZoneChangeAccessor | undefined): void { const helper = new CodeLensHelper(); for (const lens of this._lenses) { lens.dispose(helper, viewZoneChangeAccessor); @@ -300,7 +300,7 @@ export class CodeLensContribution implements IEditorContribution { groupsIndex++; codeLensIndex++; } else { - this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule())); + this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule())); codeLensIndex++; groupsIndex++; } @@ -314,7 +314,7 @@ export class CodeLensContribution implements IEditorContribution { // Create extra symbols while (groupsIndex < groups.length) { - this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule())); + this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule())); groupsIndex++; } diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index a661af5def1..622e7785aaf 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -7,7 +7,7 @@ import 'vs/css!./codelensWidget'; import * as dom from 'vs/base/browser/dom'; import { renderCodicons } from 'vs/base/common/codicons'; import { escape } from 'vs/base/common/strings'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { IViewZone, IContentWidget, IActiveCodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -17,7 +17,7 @@ import { CodeLensItem } from 'vs/editor/contrib/codelens/codelens'; import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -class CodeLensViewZone implements editorBrowser.IViewZone { +class CodeLensViewZone implements IViewZone { readonly heightInLines: number; readonly suppressMouseDown: boolean; @@ -47,7 +47,7 @@ class CodeLensViewZone implements editorBrowser.IViewZone { } } -class CodeLensContentWidget implements editorBrowser.IContentWidget { +class CodeLensContentWidget implements IContentWidget { private static _idPool: number = 0; @@ -57,14 +57,14 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { private readonly _id: string; private readonly _domNode: HTMLElement; - private readonly _editor: editorBrowser.IActiveCodeEditor; + private readonly _editor: IActiveCodeEditor; private readonly _commands = new Map(); - private _widgetPosition?: editorBrowser.IContentWidgetPosition; + private _widgetPosition?: IContentWidgetPosition; private _isEmpty: boolean = true; constructor( - editor: editorBrowser.IActiveCodeEditor, + editor: IActiveCodeEditor, className: string, line: number, ) { @@ -137,11 +137,11 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { const column = this._editor.getModel().getLineFirstNonWhitespaceColumn(line); this._widgetPosition = { position: { lineNumber: line, column: column }, - preference: [editorBrowser.ContentWidgetPositionPreference.ABOVE] + preference: [ContentWidgetPositionPreference.ABOVE] }; } - getPosition(): editorBrowser.IContentWidgetPosition | null { + getPosition(): IContentWidgetPosition | null { return this._widgetPosition || null; } } @@ -181,7 +181,7 @@ export class CodeLensHelper { export class CodeLensWidget { - private readonly _editor: editorBrowser.IActiveCodeEditor; + private readonly _editor: IActiveCodeEditor; private readonly _className: string; private readonly _viewZone!: CodeLensViewZone; private readonly _viewZoneId!: string; @@ -193,10 +193,10 @@ export class CodeLensWidget { constructor( data: CodeLensItem[], - editor: editorBrowser.IActiveCodeEditor, + editor: IActiveCodeEditor, className: string, helper: CodeLensHelper, - viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor, + viewZoneChangeAccessor: IViewZoneChangeAccessor, updateCallback: Function ) { this._editor = editor; @@ -244,7 +244,7 @@ export class CodeLensWidget { } } - dispose(helper: CodeLensHelper, viewZoneChangeAccessor?: editorBrowser.IViewZoneChangeAccessor): void { + dispose(helper: CodeLensHelper, viewZoneChangeAccessor?: IViewZoneChangeAccessor): void { this._decorationIds.forEach(helper.removeDecoration, helper); this._decorationIds = []; if (viewZoneChangeAccessor) { @@ -322,7 +322,7 @@ export class CodeLensWidget { return -1; } - update(viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { + update(viewZoneChangeAccessor: IViewZoneChangeAccessor): void { if (this.isValid()) { const range = this._editor.getModel().getDecorationRange(this._decorationIds[0]); if (range) { diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index 8626630e183..47a341a2d63 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { View } from 'vs/editor/browser/view/viewImpl'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; @@ -26,7 +26,7 @@ import { TestNotificationService } from 'vs/platform/notification/test/common/te import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; -export class TestCodeEditor extends CodeEditorWidget implements editorBrowser.ICodeEditor { +export class TestCodeEditor extends CodeEditorWidget implements ICodeEditor { //#region testing overrides protected _createConfiguration(options: editorOptions.IEditorConstructionOptions): IConfiguration { From 661bc5da418e104c25086a145f27a2095b2b2b35 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 14 Jan 2020 17:06:45 +0100 Subject: [PATCH 279/843] define constants in typescript-vscode-sh-plugin --- .../typescript-language-features/package.json | 2 +- .../src/features/semanticTokens.ts | 110 ++++++++---------- .../src/utils/api.ts | 1 - .../typescript-language-features/yarn.lock | 8 +- 4 files changed, 54 insertions(+), 67 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 681c5c4b2d1..4238a966b5b 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -19,7 +19,7 @@ "jsonc-parser": "^2.1.1", "rimraf": "^2.6.3", "semver": "5.5.1", - "typescript-vscode-sh-plugin": "^0.3.2", + "typescript-vscode-sh-plugin": "^0.5.0", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0" }, diff --git a/extensions/typescript-language-features/src/features/semanticTokens.ts b/extensions/typescript-language-features/src/features/semanticTokens.ts index 78f7dfb36a8..e34dc09de9a 100644 --- a/extensions/typescript-language-features/src/features/semanticTokens.ts +++ b/extensions/typescript-language-features/src/features/semanticTokens.ts @@ -9,7 +9,10 @@ import * as Proto from '../protocol'; import { VersionDependentRegistration } from '../utils/dependentRegistration'; import API from '../utils/api'; -const minTypeScriptVersion = API.v370; +// all constants are const +import { TokenType, TokenModifier, TokenEncodingConsts, VersionRequirement } from 'typescript-vscode-sh-plugin/lib/constants'; + +const minTypeScriptVersion = API.fromVersionString(`${VersionRequirement.major}.${VersionRequirement.minor}`); export function register(selector: vscode.DocumentSelector, client: ITypeScriptServiceClient) { return new VersionDependentRegistration(client, minTypeScriptVersion, () => { @@ -29,18 +32,16 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider { } getLegend(): vscode.SemanticTokensLegend { - const tokenTypes = []; - for (let i = 0; i < VSCodeShPlugin.TokenType._sentinel; i++) { - tokenTypes.push(VSCodeShPlugin.TokenType[i]); + if (tokenTypes.length !== TokenType._) { + console.warn('typescript-vscode-sh-plugin has added new tokens types.'); } - const tokenModifiers = []; - for (let i = 0; i < VSCodeShPlugin.TokenModifier._sentinel; i++) { - tokenModifiers.push(VSCodeShPlugin.TokenModifier[i]); + if (tokenModifiers.length !== TokenModifier._) { + console.warn('typescript-vscode-sh-plugin has added new tokens modifiers.'); } return new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers); } - async provideSemanticTokens(document: vscode.TextDocument, _options: vscode.SemanticTokensRequestOptions, token: vscode.CancellationToken): Promise { + async provideSemanticTokens(document: vscode.TextDocument, options: vscode.SemanticTokensRequestOptions, token: vscode.CancellationToken): Promise { const file = this.client.toOpenedFilePath(document); if (!file) { return null; @@ -51,8 +52,8 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider { const allTokenSpans: number[][] = []; let requestArgs: ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs[] = []; - if (_options.ranges) { - requestArgs = _options.ranges.map(r => { const start = document.offsetAt(r.start); const length = document.offsetAt(r.end) - start; return { file, start, length }; }); + if (options.ranges) { + requestArgs = options.ranges.map(r => { const start = document.offsetAt(r.start); const length = document.offsetAt(r.end) - start; return { file, start, length }; }); requestArgs = requestArgs.sort((a1, a2) => a1.start - a2.start); } else { requestArgs = [{ file, start: 0, length: document.getText().length }]; // full document @@ -81,10 +82,10 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider { const tsClassification = tokenSpan[i++]; let tokenModifiers = 0; - let tokenType = VSCodeShPlugin.getTokenTypeFromClassification(tsClassification); + let tokenType = getTokenTypeFromClassification(tsClassification); if (tokenType !== undefined) { // it's a classification as returned by the typescript-vscode-sh-plugin - tokenModifiers = VSCodeShPlugin.getTokenModifierFromClassification(tsClassification); + tokenModifiers = getTokenModifierFromClassification(tsClassification); } else { // typescript-vscode-sh-plugin is not present tokenType = tokenTypeMap[tsClassification]; @@ -108,61 +109,48 @@ class SemanticTokensProvider implements vscode.SemanticTokensProvider { } } -namespace VSCodeShPlugin { +// typescript-vscode-sh-plugin encodes type and modifiers in the classification: +// TSClassification = (TokenType + 1) << 8 + TokenModifier - // typescript-vscode-sh-plugin encodes type and modifiers in the classification: - // TSClassification = (TokenType + 1) << 8 + TokenModifier - - const TokenTypeOffset = 8; - const TokenModifierMask = (1 << TokenTypeOffset) - 1; // 0xFF - - export function getTokenTypeFromClassification(tsClassification: number): number | undefined { - if (tsClassification > TokenModifierMask) { - return (tsClassification >> TokenTypeOffset) - 1; - } - return undefined; - } - - export function getTokenModifierFromClassification(tsClassification: number) { - return tsClassification & TokenModifierMask; - } - - // Don't change TokenType and TokenModifier enums without adopting typescript-vscode-sh-plugin - export enum TokenType { - 'class', - 'enum', - 'interface', - 'namespace', - 'typeParameter', - 'type', - 'parameter', - 'variable', - 'property', - 'constant', - 'function', - 'member', - _sentinel - } - - export enum TokenModifier { - 'declaration', - 'static', - 'async', - 'readonly', - _sentinel +function getTokenTypeFromClassification(tsClassification: number): number | undefined { + if (tsClassification > TokenEncodingConsts.modifierMask) { + return (tsClassification >> TokenEncodingConsts.typeOffset) - 1; } + return undefined; } -// mapping for the original ExperimentalProtocol.ClassificationType from TypeScript (only used when plugin is not available) +function getTokenModifierFromClassification(tsClassification: number) { + return tsClassification & TokenEncodingConsts.modifierMask; +} +const tokenTypes: string[] = []; +tokenTypes[TokenType.class] = 'class'; +tokenTypes[TokenType.enum] = 'enum'; +tokenTypes[TokenType.interface] = 'interface'; +tokenTypes[TokenType.namespace] = 'namespace'; +tokenTypes[TokenType.typeParameter] = 'typeParameter'; +tokenTypes[TokenType.type] = 'type'; +tokenTypes[TokenType.parameter] = 'parameter'; +tokenTypes[TokenType.variable] = 'variable'; +tokenTypes[TokenType.property] = 'property'; +tokenTypes[TokenType.function] = 'function'; +tokenTypes[TokenType.member] = 'member'; + +const tokenModifiers: string[] = []; +tokenModifiers[TokenModifier.async] = 'async'; +tokenModifiers[TokenModifier.declaration] = 'declaration'; +tokenModifiers[TokenModifier.readonly] = 'readonly'; +tokenModifiers[TokenModifier.static] = 'static'; + +// mapping for the original ExperimentalProtocol.ClassificationType from TypeScript (only used when plugin is not available) const tokenTypeMap: number[] = []; -tokenTypeMap[ExperimentalProtocol.ClassificationType.className] = VSCodeShPlugin.TokenType.class; -tokenTypeMap[ExperimentalProtocol.ClassificationType.enumName] = VSCodeShPlugin.TokenType.enum; -tokenTypeMap[ExperimentalProtocol.ClassificationType.interfaceName] = VSCodeShPlugin.TokenType.interface; -tokenTypeMap[ExperimentalProtocol.ClassificationType.moduleName] = VSCodeShPlugin.TokenType.namespace; -tokenTypeMap[ExperimentalProtocol.ClassificationType.typeParameterName] = VSCodeShPlugin.TokenType.typeParameter; -tokenTypeMap[ExperimentalProtocol.ClassificationType.typeAliasName] = VSCodeShPlugin.TokenType.type; -tokenTypeMap[ExperimentalProtocol.ClassificationType.parameterName] = VSCodeShPlugin.TokenType.parameter; +tokenTypeMap[ExperimentalProtocol.ClassificationType.className] = TokenType.class; +tokenTypeMap[ExperimentalProtocol.ClassificationType.enumName] = TokenType.enum; +tokenTypeMap[ExperimentalProtocol.ClassificationType.interfaceName] = TokenType.interface; +tokenTypeMap[ExperimentalProtocol.ClassificationType.moduleName] = TokenType.namespace; +tokenTypeMap[ExperimentalProtocol.ClassificationType.typeParameterName] = TokenType.typeParameter; +tokenTypeMap[ExperimentalProtocol.ClassificationType.typeAliasName] = TokenType.type; +tokenTypeMap[ExperimentalProtocol.ClassificationType.parameterName] = TokenType.parameter; namespace ExperimentalProtocol { diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index 8f181f14b65..0fa41cb01a0 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -31,7 +31,6 @@ export default class API { public static readonly v340 = API.fromSimpleString('3.4.0'); public static readonly v345 = API.fromSimpleString('3.4.5'); public static readonly v350 = API.fromSimpleString('3.5.0'); - public static readonly v370 = API.fromSimpleString('3.7.0'); public static readonly v380 = API.fromSimpleString('3.8.0'); public static fromVersionString(versionString: string): API { diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index cc6e217941d..086a2a9a106 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -626,10 +626,10 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -typescript-vscode-sh-plugin@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.3.2.tgz#bde9d0eba24ca5856024811fa354a9f5a7aeebe5" - integrity sha512-XsalETsSf3y7VWxk36plqHpsbl+TUDl278HEuhPHVBcNnwuTjcIq52J/CJw84xYmxmBcTIPUgIgLLS4OE5nX2A== +typescript-vscode-sh-plugin@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.5.0.tgz#014dd928f2fa5000396147ed00792a2c901d97b9" + integrity sha512-MKqivbdkgllHS3Rab/zvXlGAxwCb1AHzgO/a8vmG6i5kExGIytwjUyXALdnnLUWS03B9eEJmIjzOz4y3MpgliQ== uri-js@^4.2.2: version "4.2.2" From 3198a2817692fdb2dd0cff06b7c1a2b2db1eca09 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 14 Jan 2020 17:35:54 +0100 Subject: [PATCH 280/843] make consistent with plugin --- .../src/modes/javascriptSemanticTokens.ts | 84 +++++++++++-------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts index 38a87159a32..2824a880d4c 100644 --- a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts +++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts @@ -6,44 +6,63 @@ import { TextDocument, SemanticTokenData } from './languageModes'; import * as ts from 'typescript'; +export function getSemanticTokenLegend() { + if (tokenTypes.length !== TokenType._) { + console.warn('TokenType has added new entries.'); + } + if (tokenModifiers.length !== TokenModifier._) { + console.warn('TokenModifier has added new entries.'); + } + return { types: tokenTypes, modifiers: tokenModifiers }; +} export function getSemanticTokens(jsLanguageService: ts.LanguageService, currentTextDocument: TextDocument, fileName: string): SemanticTokenData[] { //https://ts-ast-viewer.com/#code/AQ0g2CmAuwGbALzAJwG4BQZQGNwEMBnQ4AQQEYBmYAb2C22zgEtJwATJVTRxgcwD27AQAp8AGmAAjAJS0A9POB8+7NQ168oscAJz5wANXwAnLug2bsJmAFcTAO2XAA1MHyvgu-UdOeWbOw8ViAAvpagocBAA let resultTokens: SemanticTokenData[] = []; + const collector = (node: ts.Node, typeIdx: number, modifierSet: number) => { + resultTokens.push({ start: currentTextDocument.positionAt(node.getStart()), length: node.getWidth(), typeIdx, modifierSet }); + }; + collectTokens(jsLanguageService, fileName, { start: 0, length: currentTextDocument.getText().length }, collector); + + return resultTokens; +} + +function collectTokens(jsLanguageService: ts.LanguageService, fileName: string, span: ts.TextSpan, collector: (node: ts.Node, tokenType: number, tokenModifier: number) => void) { const program = jsLanguageService.getProgram(); if (program) { const typeChecker = program.getTypeChecker(); function visit(node: ts.Node) { + if (!node || !ts.textSpanIntersectsWith(span, node.pos, node.getFullWidth())) { + return; + } if (ts.isIdentifier(node)) { const symbol = typeChecker.getSymbolAtLocation(node); if (symbol) { let typeIdx = classifySymbol(symbol); - if (typeIdx !== undefined) { - let modifierSet = 0; if (node.parent) { const parentTypeIdx = tokenFromDeclarationMapping[node.parent.kind]; if (parentTypeIdx === typeIdx && (node.parent).name === node) { - modifierSet = TokenModifier.declaration; + modifierSet = 1 << TokenModifier.declaration; } } const decl = symbol.valueDeclaration; const modifiers = decl ? ts.getCombinedModifierFlags(decl) : 0; const nodeFlags = decl ? ts.getCombinedNodeFlags(decl) : 0; if (modifiers & ts.ModifierFlags.Static) { - modifierSet |= TokenModifier.static; + modifierSet |= 1 << TokenModifier.static; } if (modifiers & ts.ModifierFlags.Async) { - modifierSet |= TokenModifier.async; + modifierSet |= 1 << TokenModifier.async; } if ((modifiers & ts.ModifierFlags.Readonly) || (nodeFlags & ts.NodeFlags.Const) || (symbol.getFlags() & ts.SymbolFlags.EnumMember)) { - modifierSet |= TokenModifier.readonly; + modifierSet |= 1 << TokenModifier.readonly; } - resultTokens.push({ start: currentTextDocument.positionAt(node.getStart()), length: node.getWidth(), typeIdx, modifierSet }); + collector(node, typeIdx, modifierSet); } } } @@ -55,12 +74,9 @@ export function getSemanticTokens(jsLanguageService: ts.LanguageService, current visit(sourceFile); } } - - return resultTokens; } function classifySymbol(symbol: ts.Symbol) { - const flags = symbol.getFlags(); if (flags & ts.SymbolFlags.Class) { return TokenType.class; @@ -79,38 +95,32 @@ function classifySymbol(symbol: ts.Symbol) { return decl && tokenFromDeclarationMapping[decl.kind]; } - - -export function getSemanticTokenLegend() { - return { types: tokenTypes, modifiers: tokenModifiers }; +export const enum TokenType { + class, enum, interface, namespace, typeParameter, type, parameter, variable, property, function, member, _ } - -const tokenTypes: string[] = ['class', 'enum', 'interface', 'namespace', 'typeParameter', 'type', 'parameter', 'variable', 'property', 'constant', 'function', 'member']; -const tokenModifiers: string[] = ['declaration', 'static', 'async', 'readonly']; - -const enum TokenType { - 'class' = 0, - 'enum' = 1, - 'interface' = 2, - 'namespace' = 3, - 'typeParameter' = 4, - 'type' = 5, - 'parameter' = 6, - 'variable' = 7, - 'property' = 8, - 'constant' = 9, - 'function' = 10, - 'member' = 11 +export const enum TokenModifier { + declaration, static, async, readonly, _ } +const tokenTypes: string[] = []; +tokenTypes[TokenType.class] = 'class'; +tokenTypes[TokenType.enum] = 'enum'; +tokenTypes[TokenType.interface] = 'interface'; +tokenTypes[TokenType.namespace] = 'namespace'; +tokenTypes[TokenType.typeParameter] = 'typeParameter'; +tokenTypes[TokenType.type] = 'type'; +tokenTypes[TokenType.parameter] = 'parameter'; +tokenTypes[TokenType.variable] = 'variable'; +tokenTypes[TokenType.property] = 'property'; +tokenTypes[TokenType.function] = 'function'; +tokenTypes[TokenType.member] = 'member'; -const enum TokenModifier { - 'declaration' = 0x01, - 'static' = 0x02, - 'async' = 0x04, - 'readonly' = 0x08, -} +const tokenModifiers: string[] = []; +tokenModifiers[TokenModifier.async] = 'async'; +tokenModifiers[TokenModifier.declaration] = 'declaration'; +tokenModifiers[TokenModifier.readonly] = 'readonly'; +tokenModifiers[TokenModifier.static] = 'static'; const tokenFromDeclarationMapping: { [name: string]: TokenType } = { [ts.SyntaxKind.VariableDeclaration]: TokenType.variable, From 7cd3e04fb8af123ed6d995bc575a2b12c76d4586 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jan 2020 17:38:52 +0100 Subject: [PATCH 281/843] editors - favor closeEditors() over closeEditor() --- .../contrib/bulkEdit/browser/bulkEdit.contribution.ts | 8 +++++++- .../contrib/extensions/browser/extensionsViewlet.ts | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index ec51b044e9c..aacc0db7d51 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -25,6 +25,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { URI } from 'vs/base/common/uri'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { IEditorInput } from 'vs/workbench/common/editor'; function getBulkEditPane(panelService: IPanelService): BulkEditPane | undefined { let view: ViewPane | undefined; @@ -86,6 +87,7 @@ class BulkEditPreviewContribution { // (3) close preview editors for (let group of this._editorGroupsService.groups) { + let previewEditors: IEditorInput[] = []; for (let input of group.editors) { let resource: URI | undefined; @@ -96,9 +98,13 @@ class BulkEditPreviewContribution { } if (resource?.scheme === BulkEditPreviewProvider.Schema) { - group.closeEditor(input, { preserveFocus: true }); + previewEditors.push(input); } } + + if (previewEditors.length) { + group.closeEditors(previewEditors, { preserveFocus: true }); + } } } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 9accf3727af..8c450fe0b0c 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -583,9 +583,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE if (this.configurationService.getValue(CloseExtensionDetailsOnViewChangeKey)) { const promises = this.editorGroupService.groups.map(group => { const editors = group.editors.filter(input => input instanceof ExtensionsInput); - const promises = editors.map(editor => group.closeEditor(editor)); - return Promise.all(promises); + return group.closeEditors(editors); }); Promise.all(promises); From 294f3b04c5e1baa305f4ef6179506633ce4c8694 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jan 2020 18:09:12 +0100 Subject: [PATCH 282/843] text files - allow to save any existing model --- .../textfile/browser/textFileService.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index edf1aac01d9..5ee41d8fab9 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -35,6 +35,7 @@ import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; +import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; /** * The workbench file service implementation implements the raw file service spec and adds additional methods on top. @@ -519,8 +520,8 @@ export abstract class AbstractTextFileService extends Disposable implements ITex success = true; } - // Finally, if the source does not seem to be a file, we have to - // try to resolve a text model from the resource to get at the + // Next, if the source does not seem to be a file, we try to + // resolve a text model from the resource to get at the // contents and additional meta data (e.g. encoding). else if (this.textModelService.hasTextModelContentProvider(source.scheme)) { const modelReference = await this.textModelService.createModelReference(source); @@ -529,6 +530,20 @@ export abstract class AbstractTextFileService extends Disposable implements ITex modelReference.dispose(); // free up our use of the reference } + // Finally we simply check if we can find a editor model that + // would give us access to the contents. + else { + const textModel = this.modelService.getModel(source); + if (textModel) { + const model = new BaseTextEditorModel(this.modelService, this.modeService, source); + if (model.isResolved()) { + success = await this.doSaveAsTextFile(model, source, target, options); + } + + model.dispose(); // free up + } + } + // Revert the source if result is success if (success) { await this.revert(source); From 56a9ef199e349a727309a1e15b1a99af5dc0100e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jan 2020 18:13:14 +0100 Subject: [PATCH 283/843] build - see if test retry helps --- .../test/electron-main/workspacesMainService.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index e7baa0d80bd..e082679d700 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -320,7 +320,9 @@ suite('WorkspacesMainService', () => { service.deleteUntitledWorkspaceSync(workspace); }); - test('getUntitledWorkspaceSync', async () => { + test('getUntitledWorkspaceSync', async function () { + this.retries(3); + let untitled = service.getUntitledWorkspacesSync(); assert.equal(untitled.length, 0); From 441a5860c5253f4b5576e03e808b53d58cf0b24e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 14 Jan 2020 18:57:45 +0100 Subject: [PATCH 284/843] Command to show sync log --- .../userDataSync/browser/userDataSync.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 666725b9922..222643aa7b4 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -33,6 +33,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { UserDataAutoSync } from 'vs/workbench/contrib/userDataSync/browser/userDataAutoSync'; import { UserDataSyncTrigger } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger'; import { timeout } from 'vs/base/common/async'; +import { IOutputService } from 'vs/workbench/contrib/output/common/output'; +import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey('authTokenStatus', AuthTokenStatus.Initializing); const SYNC_PUSH_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-light.svg`)); @@ -63,6 +65,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @IDialogService private readonly dialogService: IDialogService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IInstantiationService instantiationService: IInstantiationService, + @IOutputService private readonly outputService: IOutputService, ) { super(); this.userDataSyncStore = getUserDataSyncStore(configurationService); @@ -425,6 +428,10 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo return null; } + private showSyncLog(): Promise { + return this.outputService.showChannel(Constants.userDataSyncLogChannelId); + } + private registerActions(): void { const turnOnSyncCommandId = 'workbench.userData.actions.syncStart'; @@ -570,5 +577,15 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }, when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`)), }); + + const showSyncLogCommandId = 'workbench.userData.actions.showSyncLog'; + CommandsRegistry.registerCommand(showSyncLogCommandId, () => this.showSyncLog()); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: showSyncLogCommandId, + title: localize('show sync log', "Sync: Show Sync Log") + }, + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)), + }); } } From 0fc545e916059d42ac782379433122f9de5e55a1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 13 Jan 2020 21:43:59 -0800 Subject: [PATCH 285/843] Also show refactor documentation in normal lightbulb menu if refactorings are returned For #86788 --- src/vs/editor/common/modes.ts | 11 +++- .../editor/contrib/codeAction/codeAction.ts | 6 +- .../contrib/codeAction/codeActionCommands.ts | 8 +-- .../contrib/codeAction/codeActionMenu.ts | 35 ++++++----- .../contrib/codeAction/codeActionModel.ts | 4 +- .../editor/contrib/codeAction/codeActionUi.ts | 20 +++---- .../contrib/codeAction/lightBulbWidget.ts | 11 ++-- .../codeAction/test/codeAction.test.ts | 20 +++---- .../codeAction/test/codeActionModel.test.ts | 9 ++- src/vs/editor/contrib/codeAction/types.ts | 8 +-- .../editor/contrib/hover/modesContentHover.ts | 13 ++-- .../api/browser/mainThreadSaveParticipant.ts | 4 +- .../common/documentationContribution.ts | 59 ++++++++----------- .../markers/browser/markersTreeViewer.ts | 7 ++- .../api/extHostLanguageFeatures.test.ts | 9 ++- 15 files changed, 116 insertions(+), 108 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 78495217dc3..66f65392bd6 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -555,8 +555,8 @@ export interface CodeAction { /** * @internal */ -export const enum CodeActionTrigger { - Automatic = 1, +export const enum CodeActionTriggerType { + Auto = 1, Manual = 2, } @@ -565,7 +565,7 @@ export const enum CodeActionTrigger { */ export interface CodeActionContext { only?: string; - trigger: CodeActionTrigger; + trigger: CodeActionTriggerType; } export interface CodeActionList extends IDisposable { @@ -587,6 +587,11 @@ export interface CodeActionProvider { * Optional list of CodeActionKinds that this provider returns. */ providedCodeActionKinds?: ReadonlyArray; + + /** + * @internal + */ + _getAdditionalMenuItems?(context: CodeActionContext, actions: readonly CodeAction[]): Command[]; } /** diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 37744867d8c..b0a9aa8051f 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -15,7 +15,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { CodeActionFilter, CodeActionKind, CodeActionTrigger, CodeActionTriggerType, filtersAction, mayIncludeActionsOfKind } from './types'; +import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './types'; export const codeActionCommandId = 'editor.action.codeAction'; export const refactorCommandId = 'editor.action.refactor'; @@ -70,7 +70,7 @@ export function getCodeActions( const codeActionContext: modes.CodeActionContext = { only: filter.include?.value, - trigger: trigger.type === CodeActionTriggerType.Manual ? modes.CodeActionTrigger.Manual : modes.CodeActionTrigger.Automatic + trigger: trigger.type, }; const cts = new TextModelCancellationTokenSource(model, token); @@ -149,7 +149,7 @@ registerLanguageCommand('_executeCodeActionProvider', async function (accessor, const codeActionSet = await getCodeActions( model, validatedRangeOrSelection, - { type: CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include: kind && kind.value ? new CodeActionKind(kind.value) : undefined } }, + { type: modes.CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include: kind && kind.value ? new CodeActionKind(kind.value) : undefined } }, CancellationToken.None); setTimeout(() => codeActionSet.dispose(), 100); diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 003830553f6..185ebd86867 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -15,7 +15,7 @@ import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IPosition } from 'vs/editor/common/core/position'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { CodeAction } from 'vs/editor/common/modes'; +import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes'; import { codeActionCommandId, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionUi } from 'vs/editor/contrib/codeAction/codeActionUi'; import { MessageController } from 'vs/editor/contrib/message/messageController'; @@ -29,7 +29,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CodeActionModel, CodeActionsState, SUPPORTED_CODE_ACTIONS } from './codeActionModel'; -import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger, CodeActionTriggerType } from './types'; +import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger } from './types'; function contextKeyForSupportedActions(kind: CodeActionKind) { return ContextKeyExpr.regex( @@ -109,8 +109,8 @@ export class QuickFixController extends Disposable implements IEditorContributio this._ui.getValue().update(newState); } - public showCodeActions(actions: CodeActionSet, at: IAnchor | IPosition) { - return this._ui.getValue().showCodeActionList(actions, at, { includeDisabledActions: false }); + public showCodeActions(trigger: CodeActionTrigger, actions: CodeActionSet, at: IAnchor | IPosition) { + return this._ui.getValue().showCodeActionList(trigger, actions, at, { includeDisabledActions: false }); } public manualTriggerAtCurrentPosition( diff --git a/src/vs/editor/contrib/codeAction/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/codeActionMenu.ts index 7be72d3d2e3..e732dec94c3 100644 --- a/src/vs/editor/contrib/codeAction/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/codeActionMenu.ts @@ -14,9 +14,9 @@ import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { ScrollType } from 'vs/editor/common/editorCommon'; -import { CodeAction } from 'vs/editor/common/modes'; +import { CodeAction, CodeActionProviderRegistry } from 'vs/editor/common/modes'; import { codeActionCommandId, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/codeAction'; -import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionKind } from 'vs/editor/contrib/codeAction/types'; +import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionKind, CodeActionTrigger } from 'vs/editor/contrib/codeAction/types'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; @@ -68,7 +68,7 @@ export class CodeActionMenu extends Disposable { return this._visible; } - public async show(codeActions: CodeActionSet, at: IAnchor | IPosition, options: CodeActionShowOptions): Promise { + public async show(trigger: CodeActionTrigger, codeActions: CodeActionSet, at: IAnchor | IPosition, options: CodeActionShowOptions): Promise { const actionsToShow = options.includeDisabledActions ? codeActions.allActions : codeActions.validActions; if (!actionsToShow.length) { this._visible = false; @@ -84,7 +84,7 @@ export class CodeActionMenu extends Disposable { this._visible = true; this._showingActions.value = codeActions; - const menuActions = this.getMenuActions(actionsToShow); + const menuActions = this.getMenuActions(trigger, actionsToShow); const anchor = Position.isIPosition(at) ? this._toCoords(at) : at || { x: 0, y: 0 }; const resolver = this._keybindingResolver.getResolver(); @@ -101,19 +101,26 @@ export class CodeActionMenu extends Disposable { }); } - private getMenuActions(actionsToShow: readonly CodeAction[]): IAction[] { - const allActions = actionsToShow - .map(action => new CodeActionAction(action, () => this._delegate.onSelectCodeAction(action))); + private getMenuActions(trigger: CodeActionTrigger, actionsToShow: readonly CodeAction[]): IAction[] { + const toCodeActionAction = (action: CodeAction): CodeActionAction => new CodeActionAction(action, () => this._delegate.onSelectCodeAction(action)); - // Treat documentation actions as special - const result: IAction[] = allActions - .filter(action => !action.action.kind || !CodeActionKind.RefactorDocumentation.contains(new CodeActionKind(action.action.kind))); + const result: IAction[] = actionsToShow + .map(toCodeActionAction); - const documentationActions = allActions - .filter(action => action.action.kind && CodeActionKind.RefactorDocumentation.contains(new CodeActionKind(action.action.kind))); - if (documentationActions.length) { - result.push(new Separator(), ...documentationActions); + const model = this._editor.getModel(); + if (model && result.length) { + for (const provider of CodeActionProviderRegistry.all(model)) { + if (provider._getAdditionalMenuItems) { + const items = provider._getAdditionalMenuItems({ trigger: trigger.type, only: trigger.filter?.include?.value }, actionsToShow); + if (items.length) { + result.push(new Separator(), ...items.map(command => toCodeActionAction({ + title: command.title, + command: command, + }))); + } + } + } } return result; diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index 0f834c0e29f..e8645271f35 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -11,12 +11,12 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { CodeActionProviderRegistry } from 'vs/editor/common/modes'; +import { CodeActionProviderRegistry, CodeActionTriggerType } from 'vs/editor/common/modes'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { getCodeActions, CodeActionSet } from './codeAction'; -import { CodeActionTrigger, CodeActionTriggerType } from './types'; +import { CodeActionTrigger } from './types'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { isEqual } from 'vs/base/common/resources'; diff --git a/src/vs/editor/contrib/codeAction/codeActionUi.ts b/src/vs/editor/contrib/codeAction/codeActionUi.ts index 6f743d1d76e..7d2aeff74f3 100644 --- a/src/vs/editor/contrib/codeAction/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/codeActionUi.ts @@ -10,14 +10,14 @@ import { Lazy } from 'vs/base/common/lazy'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition } from 'vs/editor/common/core/position'; -import { CodeAction } from 'vs/editor/common/modes'; +import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes'; import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; import { MessageController } from 'vs/editor/contrib/message/messageController'; -import { CodeActionsState } from './codeActionModel'; -import { CodeActionMenu, CodeActionShowOptions } from './codeActionMenu'; -import { LightBulbWidget } from './lightBulbWidget'; -import { CodeActionAutoApply, CodeActionTrigger, CodeActionTriggerType } from './types'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { CodeActionMenu, CodeActionShowOptions } from './codeActionMenu'; +import { CodeActionsState } from './codeActionModel'; +import { LightBulbWidget } from './lightBulbWidget'; +import { CodeActionAutoApply, CodeActionTrigger } from './types'; export class CodeActionUi extends Disposable { @@ -46,7 +46,7 @@ export class CodeActionUi extends Disposable { this._lightBulbWidget = new Lazy(() => { const widget = this._register(instantiationService.createInstance(LightBulbWidget, this._editor, quickFixActionId, preferredFixActionId)); - this._register(widget.onClick(e => this.showCodeActionList(e.actions, e, { includeDisabledActions: false }))); + this._register(widget.onClick(e => this.showCodeActionList(e.trigger, e.actions, e, { includeDisabledActions: false }))); return widget; }); } @@ -65,7 +65,7 @@ export class CodeActionUi extends Disposable { return; } - this._lightBulbWidget.getValue().update(actions, newState.position); + this._lightBulbWidget.getValue().update(actions, newState.trigger, newState.position); if (newState.trigger.type === CodeActionTriggerType.Manual) { if (newState.trigger.filter?.include) { // Triggered for specific scope @@ -103,7 +103,7 @@ export class CodeActionUi extends Disposable { } this._activeCodeActions.value = actions; - this._codeActionWidget.getValue().show(actions, newState.position, { includeDisabledActions }); + this._codeActionWidget.getValue().show(newState.trigger, actions, newState.position, { includeDisabledActions }); } else { // auto magically triggered if (this._codeActionWidget.getValue().isVisible) { @@ -143,7 +143,7 @@ export class CodeActionUi extends Disposable { return undefined; } - public async showCodeActionList(actions: CodeActionSet, at: IAnchor | IPosition, options: CodeActionShowOptions): Promise { - this._codeActionWidget.getValue().show(actions, at, options); + public async showCodeActionList(trigger: CodeActionTrigger, actions: CodeActionSet, at: IAnchor | IPosition, options: CodeActionShowOptions): Promise { + this._codeActionWidget.getValue().show(trigger, actions, at, options); } } diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 66c9a7786f0..cdabd207989 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -18,6 +18,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { editorLightBulbForeground, editorLightBulbAutoFixForeground } from 'vs/platform/theme/common/colorRegistry'; import { Gesture } from 'vs/base/browser/touch'; +import type { CodeActionTrigger } from 'vs/editor/contrib/codeAction/types'; namespace LightBulbState { @@ -33,6 +34,7 @@ namespace LightBulbState { constructor( public readonly actions: CodeActionSet, + public readonly trigger: CodeActionTrigger, public readonly editorPosition: IPosition, public readonly widgetPosition: IContentWidgetPosition, ) { } @@ -48,7 +50,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget { private readonly _domNode: HTMLDivElement; - private readonly _onClick = this._register(new Emitter<{ x: number; y: number; actions: CodeActionSet; }>()); + private readonly _onClick = this._register(new Emitter<{ x: number; y: number; actions: CodeActionSet; trigger: CodeActionTrigger }>()); public readonly onClick = this._onClick.event; private _state: LightBulbState.State = LightBulbState.Hidden; @@ -95,7 +97,8 @@ export class LightBulbWidget extends Disposable implements IContentWidget { this._onClick.fire({ x: e.posx, y: top + height + pad, - actions: this.state.actions + actions: this.state.actions, + trigger: this.state.trigger, }); })); this._register(dom.addDisposableListener(this._domNode, 'mouseenter', (e: MouseEvent) => { @@ -139,7 +142,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget { return this._state.type === LightBulbState.Type.Showing ? this._state.widgetPosition : null; } - public update(actions: CodeActionSet, atPosition: IPosition) { + public update(actions: CodeActionSet, trigger: CodeActionTrigger, atPosition: IPosition) { if (actions.validActions.length <= 0) { return this.hide(); } @@ -177,7 +180,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget { } } - this.state = new LightBulbState.Showing(actions, atPosition, { + this.state = new LightBulbState.Showing(actions, trigger, atPosition, { position: { lineNumber: effectiveLineNumber, column: 1 }, preference: LightBulbWidget._posPref }); diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index bd12d41230d..8a829bff0ca 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { TextModel } from 'vs/editor/common/model/textModel'; import * as modes from 'vs/editor/common/modes'; import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; -import { CodeActionKind, CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types'; +import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -125,7 +125,7 @@ suite('CodeAction', () => { testData.tsLint.abc ]; - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Manual }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None); assert.equal(actions.length, 6); assert.deepEqual(actions, expected); }); @@ -140,20 +140,20 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None); assert.equal(actions.length, 2); assert.strictEqual(actions[0].title, 'a'); assert.strictEqual(actions[1].title, 'a.b'); } { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a.b'); } { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None); assert.equal(actions.length, 0); } }); @@ -172,7 +172,7 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a'); }); @@ -186,13 +186,13 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'b'); } { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a'); } @@ -209,7 +209,7 @@ suite('CodeAction', () => { { const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { - type: CodeActionTriggerType.Auto, filter: { + type: modes.CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source.append('test'), excludes: [CodeActionKind.Source], includeSourceActions: true, @@ -234,7 +234,7 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { - type: CodeActionTriggerType.Auto, + type: modes.CodeActionTriggerType.Auto, filter: { include: CodeActionKind.QuickFix } diff --git a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts index d77f4010da3..c605ed49ab6 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts @@ -15,7 +15,6 @@ import { CodeActionModel, CodeActionsState } from 'vs/editor/contrib/codeAction/ import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { MarkerService } from 'vs/platform/markers/common/markerService'; -import { CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types'; const testProvider = { provideCodeActions(): modes.CodeActionList { @@ -60,7 +59,7 @@ suite('CodeActionModel', () => { disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { assertType(e.type === CodeActionsState.Type.Triggered); - assert.strictEqual(e.trigger.type, CodeActionTriggerType.Auto); + assert.strictEqual(e.trigger.type, modes.CodeActionTriggerType.Auto); assert.ok(e.actions); e.actions.then(fixes => { @@ -101,7 +100,7 @@ suite('CodeActionModel', () => { disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { assertType(e.type === CodeActionsState.Type.Triggered); - assert.equal(e.trigger.type, CodeActionTriggerType.Auto); + assert.equal(e.trigger.type, modes.CodeActionTriggerType.Auto); assert.ok(e.actions); e.actions.then(fixes => { model.dispose(); @@ -139,7 +138,7 @@ suite('CodeActionModel', () => { disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { assertType(e.type === CodeActionsState.Type.Triggered); - assert.equal(e.trigger.type, CodeActionTriggerType.Auto); + assert.equal(e.trigger.type, modes.CodeActionTriggerType.Auto); const selection = e.rangeOrSelection; assert.deepEqual(selection.selectionStartLineNumber, 1); assert.deepEqual(selection.selectionStartColumn, 1); @@ -164,7 +163,7 @@ suite('CodeActionModel', () => { disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { assertType(e.type === CodeActionsState.Type.Triggered); - assert.equal(e.trigger.type, CodeActionTriggerType.Auto); + assert.equal(e.trigger.type, modes.CodeActionTriggerType.Auto); ++triggerCount; // give time for second trigger before completing test diff --git a/src/vs/editor/contrib/codeAction/types.ts b/src/vs/editor/contrib/codeAction/types.ts index 606d9b8eced..391a389daf9 100644 --- a/src/vs/editor/contrib/codeAction/types.ts +++ b/src/vs/editor/contrib/codeAction/types.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { startsWith } from 'vs/base/common/strings'; -import { CodeAction } from 'vs/editor/common/modes'; +import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes'; import { Position } from 'vs/editor/common/core/position'; export class CodeActionKind { @@ -14,7 +14,6 @@ export class CodeActionKind { public static readonly Empty = new CodeActionKind(''); public static readonly QuickFix = new CodeActionKind('quickfix'); public static readonly Refactor = new CodeActionKind('refactor'); - public static readonly RefactorDocumentation = new CodeActionKind('refactor.documentation'); public static readonly Source = new CodeActionKind('source'); public static readonly SourceOrganizeImports = CodeActionKind.Source.append('organizeImports'); public static readonly SourceFixAll = CodeActionKind.Source.append('fixAll'); @@ -102,11 +101,6 @@ export function filtersAction(filter: CodeActionFilter, action: CodeAction): boo return true; } -export const enum CodeActionTriggerType { - Auto, - Manual -} - export interface CodeActionTrigger { readonly type: CodeActionTriggerType; readonly filter?: CodeActionFilter; diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 95b8e9813c8..ef800dc9f59 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -13,7 +13,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { DocumentColorProvider, Hover as MarkdownHover, HoverProviderRegistry, IColor, TokenizationRegistry } from 'vs/editor/common/modes'; +import { DocumentColorProvider, Hover as MarkdownHover, HoverProviderRegistry, IColor, TokenizationRegistry, CodeActionTriggerType } from 'vs/editor/common/modes'; import { getColorPresentations } from 'vs/editor/contrib/colorPicker/color'; import { ColorDetector } from 'vs/editor/contrib/colorPicker/colorDetector'; import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel'; @@ -34,7 +34,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; import { QuickFixAction, QuickFixController } from 'vs/editor/contrib/codeAction/codeActionCommands'; -import { CodeActionKind, CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types'; +import { CodeActionKind, CodeActionTrigger } from 'vs/editor/contrib/codeAction/types'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -186,6 +186,11 @@ class ModesContentComputer implements IHoverComputer { } } +const markerCodeActionTrigger: CodeActionTrigger = { + type: CodeActionTriggerType.Manual, + filter: { include: CodeActionKind.QuickFix } +}; + export class ModesContentHoverWidget extends ContentHoverWidget { static readonly ID = 'editor.contrib.modesContentHoverWidget'; @@ -575,7 +580,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { showing = true; const controller = QuickFixController.get(this._editor); const elementPosition = dom.getDomNodePagePosition(target); - controller.showCodeActions(actions, { + controller.showCodeActions(markerCodeActionTrigger, actions, { x: elementPosition.left + 6, y: elementPosition.top + elementPosition.height + 6 }); @@ -592,7 +597,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { return getCodeActions( this._editor.getModel()!, new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn), - { type: CodeActionTriggerType.Manual, filter: { include: CodeActionKind.QuickFix } }, + markerCodeActionTrigger, cancellationToken); }); } diff --git a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts index 667865985c1..b8f66352941 100644 --- a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts @@ -14,11 +14,11 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; -import { CodeAction } from 'vs/editor/common/modes'; +import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes'; import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; -import { CodeActionKind, CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types'; +import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { localize } from 'vs/nls'; diff --git a/src/vs/workbench/contrib/codeActions/common/documentationContribution.ts b/src/vs/workbench/contrib/codeActions/common/documentationContribution.ts index 17cf4fa1d9f..9c2b75c277d 100644 --- a/src/vs/workbench/contrib/codeActions/common/documentationContribution.ts +++ b/src/vs/workbench/contrib/codeActions/common/documentationContribution.ts @@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; -import { CodeAction, CodeActionContext, CodeActionList, CodeActionProvider, CodeActionProviderRegistry } from 'vs/editor/common/modes'; +import * as modes from 'vs/editor/common/modes'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -16,7 +16,7 @@ import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensi import { DocumentationExtensionPoint } from './documentationExtensionPoint'; -export class CodeActionDocumentationContribution extends Disposable implements IWorkbenchContribution, CodeActionProvider { +export class CodeActionDocumentationContribution extends Disposable implements IWorkbenchContribution, modes.CodeActionProvider { private contributions: { title: string; @@ -24,13 +24,18 @@ export class CodeActionDocumentationContribution extends Disposable implements I command: string; }[] = []; + private readonly emptyCodeActionsList = { + actions: [], + dispose: () => { } + }; + constructor( extensionPoint: IExtensionPoint, @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { super(); - CodeActionProviderRegistry.register('*', this); + this._register(modes.CodeActionProviderRegistry.register('*', this)); extensionPoint.setHandler(points => { this.contributions = []; @@ -56,36 +61,24 @@ export class CodeActionDocumentationContribution extends Disposable implements I }); } - async provideCodeActions(_model: ITextModel, _range: Range | Selection, context: CodeActionContext, _token: CancellationToken): Promise { - if (CodeActionKind.Refactor.value !== context.only) { - return { - actions: [], - dispose: () => { } - }; - } - - const actions: CodeAction[] = []; - - for (const contribution of this.contributions) { - if (!this.contextKeyService.contextMatchesRules(contribution.when)) { - continue; - } - - actions.push({ - title: contribution.title, - kind: CodeActionKind.RefactorDocumentation.value, - command: { - id: contribution.command, - title: contribution.title - } - }); - } - - return { - actions, - dispose: () => { } - }; + async provideCodeActions(_model: ITextModel, _range: Range | Selection, context: modes.CodeActionContext, _token: CancellationToken): Promise { + return this.emptyCodeActionsList; } - public readonly providedCodeActionKinds = [CodeActionKind.RefactorDocumentation.value] as const; + public _getAdditionalMenuItems(context: modes.CodeActionContext, actions: readonly modes.CodeAction[]): modes.Command[] { + if (context.only !== CodeActionKind.Refactor.value) { + if (!actions.some(action => action.kind && CodeActionKind.Refactor.contains(new CodeActionKind(action.kind)))) { + return []; + } + } + + return this.contributions + .filter(contribution => this.contextKeyService.contextMatchesRules(contribution.when)) + .map(contribution => { + return { + id: contribution.command, + title: contribution.title + }; + }); + } } diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index d7d54627f04..3e5306bc9ed 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -37,11 +37,12 @@ import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/com import { IModelService } from 'vs/editor/common/services/modelService'; import { Range } from 'vs/editor/common/core/range'; import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; -import { CodeActionKind, CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types'; +import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; +import { CodeActionTriggerType } from 'vs/editor/common/modes'; export type TreeElement = ResourceMarkers | Marker | RelatedInformation; @@ -548,7 +549,9 @@ export class MarkerViewModel extends Disposable { if (model) { if (!this.codeActionsPromise) { this.codeActionsPromise = createCancelablePromise(cancellationToken => { - return getCodeActions(model, new Range(this.marker.range.startLineNumber, this.marker.range.startColumn, this.marker.range.endLineNumber, this.marker.range.endColumn), { type: CodeActionTriggerType.Manual, filter: { include: CodeActionKind.QuickFix } }, cancellationToken).then(actions => { + return getCodeActions(model, new Range(this.marker.range.startLineNumber, this.marker.range.startColumn, this.marker.range.endLineNumber, this.marker.range.endColumn), { + type: CodeActionTriggerType.Manual, filter: { include: CodeActionKind.QuickFix } + }, cancellationToken).then(actions => { return this._register(actions); }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index 8e6fb02412c..b4b04e1cc83 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -47,7 +47,6 @@ import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { dispose } from 'vs/base/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { CodeActionTriggerType } from 'vs/editor/contrib/codeAction/types'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = EditorModel.createFromString( @@ -590,7 +589,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Manual }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None); assert.equal(actions.length, 2); const [first, second] = actions; assert.equal(first.title, 'Testing1'); @@ -614,7 +613,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Manual }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None); assert.equal(actions.length, 1); const [first] = actions; assert.equal(first.title, 'Testing1'); @@ -637,7 +636,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Manual }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None); assert.equal(actions.length, 1); }); @@ -655,7 +654,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Manual }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None); assert.equal(actions.length, 1); }); From 95793304ccd41a2e19473528a70255a149e40136 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 14 Jan 2020 13:03:59 -0800 Subject: [PATCH 286/843] Also show extract to function as disabled in js/ts Currently we only show `extract constant` --- .../src/features/refactor.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index 017ea358b2a..c866665f041 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -313,11 +313,26 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { private appendInvalidActions(actions: vscode.CodeAction[]): vscode.CodeAction[] { if (!actions.some(action => action.kind && Extract_Constant.kind.contains(action.kind))) { - const disabledAction = new vscode.CodeAction('Extract to constant', Extract_Constant.kind); + const disabledAction = new vscode.CodeAction( + localize('extractConstant.disabled.title', "Extract to constant"), + Extract_Constant.kind); + disabledAction.disabled = { - reason: localize('extract.disabled', "The current selection cannot be extracted"), + reason: localize('extractConstant.disabled.reason', "The current selection cannot be extracted"), }; disabledAction.isPreferred = true; + + actions.push(disabledAction); + } + + if (!actions.some(action => action.kind && Extract_Function.kind.contains(action.kind))) { + const disabledAction = new vscode.CodeAction( + localize('extractFunction.disabled.title', "Extract to function"), + Extract_Function.kind); + + disabledAction.disabled = { + reason: localize('extractFunction.disabled.reason', "The current selection cannot be extracted"), + }; actions.push(disabledAction); } return actions; From b13740b4f38a93500fc967884ddb793c70b8022c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 14 Jan 2020 22:29:09 +0100 Subject: [PATCH 287/843] #88637 temp fix --- src/vs/workbench/contrib/debug/browser/debug.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 061cd5dcd61..5b666732de9 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -87,7 +87,7 @@ const openPanelKb: IKeybindings = { const VIEW_CONTAINER: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: DEBUG_PANEL_ID, - name: nls.localize('runAndDebug', "Run and Debug"), + name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), focusCommand: { id: OpenDebugPanelAction.ID, From 60beab2535f09dab971d6049fa5c0bbd0631339e Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Tue, 14 Jan 2020 14:50:37 -0800 Subject: [PATCH 288/843] Move settings sync auth into built in extension --- extensions/vscode-account/.vscodeignore | 10 + .../vscode-account/media}/auth.css | 0 .../vscode-account/media}/auth.html | 3 + extensions/vscode-account/package.json | 30 + extensions/vscode-account/src/AADHelper.ts | 245 +++++++ .../vscode-account/src}/authServer.ts | 40 +- extensions/vscode-account/src/extension.ts | 38 + extensions/vscode-account/src/keychain.ts | 66 ++ extensions/vscode-account/src/utils.ts | 8 + .../vscode-account/src/vscode.proposed.d.ts | 46 ++ extensions/vscode-account/tsconfig.json | 24 + extensions/vscode-account/yarn.lock | 658 ++++++++++++++++++ .../sharedProcess/sharedProcessMain.ts | 14 +- src/vs/editor/common/modes.ts | 19 + src/vs/platform/auth/common/auth.ts | 31 - src/vs/platform/auth/common/authTokenIpc.ts | 31 - .../auth/electron-browser/authTokenService.ts | 276 -------- .../platform/product/common/productService.ts | 7 - .../common/userDataAuthTokenService.ts | 33 + .../userDataSync/common/userDataAutoSync.ts | 22 +- .../userDataSync/common/userDataSync.ts | 11 + .../userDataSync/common/userDataSyncIpc.ts | 21 +- .../common/userDataSyncService.ts | 22 +- .../common/userDataSyncStoreService.ts | 6 +- .../electron-browser/userDataAutoSync.ts | 5 +- src/vs/vscode.proposed.d.ts | 23 +- .../api/browser/extensionHost.contribution.ts | 1 + .../api/browser/mainThreadAuthentication.ts | 69 ++ .../workbench/api/common/extHost.api.impl.ts | 8 + .../workbench/api/common/extHost.protocol.ts | 17 +- .../api/common/extHostAuthentication.ts | 83 +++ .../userDataSync/browser/userDataAutoSync.ts | 5 +- .../userDataSync/browser/userDataSync.ts | 157 +++-- .../authToken/browser/authTokenService.ts | 74 -- .../electron-browser/authTokenService.ts | 61 -- .../browser/authenticationService.ts | 96 +++ .../userDataAuthTokenService.ts | 37 + src/vs/workbench/workbench.desktop.main.ts | 3 +- src/vs/workbench/workbench.web.main.ts | 5 +- 39 files changed, 1729 insertions(+), 576 deletions(-) create mode 100644 extensions/vscode-account/.vscodeignore rename {src/vs/platform/auth/common => extensions/vscode-account/media}/auth.css (100%) rename {src/vs/platform/auth/common => extensions/vscode-account/media}/auth.html (99%) create mode 100644 extensions/vscode-account/package.json create mode 100644 extensions/vscode-account/src/AADHelper.ts rename {src/vs/platform/auth/electron-browser => extensions/vscode-account/src}/authServer.ts (81%) create mode 100644 extensions/vscode-account/src/extension.ts create mode 100644 extensions/vscode-account/src/keychain.ts create mode 100644 extensions/vscode-account/src/utils.ts create mode 100644 extensions/vscode-account/src/vscode.proposed.d.ts create mode 100644 extensions/vscode-account/tsconfig.json create mode 100644 extensions/vscode-account/yarn.lock delete mode 100644 src/vs/platform/auth/common/auth.ts delete mode 100644 src/vs/platform/auth/common/authTokenIpc.ts delete mode 100644 src/vs/platform/auth/electron-browser/authTokenService.ts create mode 100644 src/vs/platform/userDataSync/common/userDataAuthTokenService.ts create mode 100644 src/vs/workbench/api/browser/mainThreadAuthentication.ts create mode 100644 src/vs/workbench/api/common/extHostAuthentication.ts delete mode 100644 src/vs/workbench/services/authToken/browser/authTokenService.ts delete mode 100644 src/vs/workbench/services/authToken/electron-browser/authTokenService.ts create mode 100644 src/vs/workbench/services/authentication/browser/authenticationService.ts create mode 100644 src/vs/workbench/services/userDataSync/electron-browser/userDataAuthTokenService.ts diff --git a/extensions/vscode-account/.vscodeignore b/extensions/vscode-account/.vscodeignore new file mode 100644 index 00000000000..ed3f9d37c1f --- /dev/null +++ b/extensions/vscode-account/.vscodeignore @@ -0,0 +1,10 @@ +.vscode/** +.vscode-test/** +out/test/** +src/** +.gitignore +vsc-extension-quickstart.md +**/tsconfig.json +**/tslint.json +**/*.map +**/*.ts \ No newline at end of file diff --git a/src/vs/platform/auth/common/auth.css b/extensions/vscode-account/media/auth.css similarity index 100% rename from src/vs/platform/auth/common/auth.css rename to extensions/vscode-account/media/auth.css diff --git a/src/vs/platform/auth/common/auth.html b/extensions/vscode-account/media/auth.html similarity index 99% rename from src/vs/platform/auth/common/auth.html rename to extensions/vscode-account/media/auth.html index 8fe3e50e7b7..0fcba4e3c62 100644 --- a/src/vs/platform/auth/common/auth.html +++ b/extensions/vscode-account/media/auth.html @@ -1,6 +1,7 @@ + @@ -8,6 +9,7 @@ + Visual Studio Code @@ -32,4 +34,5 @@ } + diff --git a/extensions/vscode-account/package.json b/extensions/vscode-account/package.json new file mode 100644 index 00000000000..46b6c92ed36 --- /dev/null +++ b/extensions/vscode-account/package.json @@ -0,0 +1,30 @@ +{ + "name": "login", + "publisher": "vscode", + "displayName": "Account", + "description": "", + "version": "0.0.1", + "engines": { + "vscode": "^1.42.0" + }, + "categories": [ + "Other" + ], + "enableProposedApi": true, + "activationEvents": [ + "*" + ], + "main": "./out/extension.js", + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./" + }, + "devDependencies": { + "typescript": "^3.7.4", + "tslint": "^5.12.1", + "@types/node": "^10.12.21", + "@types/keytar": "^4.0.1", + "@types/vscode": "^1.41.0" + } +} diff --git a/extensions/vscode-account/src/AADHelper.ts b/extensions/vscode-account/src/AADHelper.ts new file mode 100644 index 00000000000..b15aec32697 --- /dev/null +++ b/extensions/vscode-account/src/AADHelper.ts @@ -0,0 +1,245 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as crypto from 'crypto'; +import * as vscode from 'vscode'; +import * as https from 'https'; +import * as querystring from 'querystring'; +import { keychain } from './keychain'; +import { toBase64UrlEncoding } from './utils'; +import { createServer, startServer } from './authServer'; + +const redirectUrl = 'https://vscode-redirect.azurewebsites.net/'; +const loginEndpointUrl = 'https://login.microsoftonline.com/'; +const clientId = 'aebc6443-996d-45c2-90f0-388ff96faa56'; +const scope = 'https://management.core.windows.net/.default offline_access'; +const tenant = 'common'; + +interface IToken { + expiresIn: string; // How long access token is valid, in seconds + accessToken: string; + refreshToken: string; +} + +export const onDidChangeAccounts = new vscode.EventEmitter(); + +export class AzureActiveDirectoryService { + private _token: IToken | undefined; + private _refreshTimeout: NodeJS.Timeout | undefined; + + public async initialize(): Promise { + const existingRefreshToken = await keychain.getToken(); + if (existingRefreshToken) { + await this.refreshToken(existingRefreshToken); + } + } + + private tokenToAccount(token: IToken): vscode.Account { + return { + id: '', + accessToken: token.accessToken, + displayName: this.getDisplayNameFromToken(token.accessToken) + }; + } + + private getDisplayNameFromToken(accessToken: string): string { + let displayName = 'user@example.com'; + try { + // TODO fixme + displayName = JSON.parse(atob(accessToken.split('.')[1])); + } catch (e) { + // Fall back to example display name + } + + return displayName; + } + + get accounts(): vscode.Account[] { + return this._token ? [this.tokenToAccount(this._token)] : []; + } + + public async login(): Promise { + const nonce = crypto.randomBytes(16).toString('base64'); + const { server, redirectPromise, codePromise } = createServer(nonce); + + let token: IToken | undefined; + try { + const port = await startServer(server); + vscode.env.openExternal(vscode.Uri.parse(`http://localhost:${port}/signin?nonce=${encodeURIComponent(nonce)}`)); + + const redirectReq = await redirectPromise; + if ('err' in redirectReq) { + const { err, res } = redirectReq; + res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` }); + res.end(); + throw err; + } + + const host = redirectReq.req.headers.host || ''; + const updatedPortStr = (/^[^:]+:(\d+)$/.exec(Array.isArray(host) ? host[0] : host) || [])[1]; + const updatedPort = updatedPortStr ? parseInt(updatedPortStr, 10) : port; + + const state = `${updatedPort},${encodeURIComponent(nonce)}`; + + const codeVerifier = toBase64UrlEncoding(crypto.randomBytes(32).toString('base64')); + const codeChallenge = toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64')); + const loginUrl = `${loginEndpointUrl}${tenant}/oauth2/v2.0/authorize?response_type=code&response_mode=query&client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUrl)}&state=${state}&scope=${encodeURIComponent(scope)}&prompt=select_account&code_challenge_method=S256&code_challenge=${codeChallenge}`; + + await redirectReq.res.writeHead(302, { Location: loginUrl }); + redirectReq.res.end(); + + const codeRes = await codePromise; + const res = codeRes.res; + + try { + if ('err' in codeRes) { + throw codeRes.err; + } + token = await this.exchangeCodeForToken(codeRes.code, codeVerifier); + this.setToken(token); + res.writeHead(302, { Location: '/' }); + res.end(); + } catch (err) { + res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` }); + res.end(); + } + } finally { + setTimeout(() => { + server.close(); + }, 5000); + } + } + + private async setToken(token: IToken): Promise { + this._token = token; + + if (this._refreshTimeout) { + clearTimeout(this._refreshTimeout); + } + + this._refreshTimeout = setTimeout(async () => { + try { + await this.refreshToken(token.refreshToken); + } catch (e) { + vscode.window.showErrorMessage(`You have been signed out.`); + this._token = undefined; + } finally { + onDidChangeAccounts.fire(this.accounts); + } + }, 1000 * (parseInt(token.expiresIn) - 10)); + + await keychain.setToken(token.refreshToken); + } + + private async exchangeCodeForToken(code: string, codeVerifier: string): Promise { + return new Promise((resolve: (value: IToken) => void, reject) => { + try { + const postData = querystring.stringify({ + grant_type: 'authorization_code', + code: code, + client_id: clientId, + scope: scope, + code_verifier: codeVerifier, + redirect_uri: redirectUrl + }); + + const tokenUrl = vscode.Uri.parse(`${loginEndpointUrl}${tenant}/oauth2/v2.0/token`); + + const post = https.request({ + host: tokenUrl.authority, + path: tokenUrl.path, + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': postData.length + } + }, result => { + const buffer: Buffer[] = []; + result.on('data', (chunk: Buffer) => { + buffer.push(chunk); + }); + result.on('end', () => { + if (result.statusCode === 200) { + const json = JSON.parse(Buffer.concat(buffer).toString()); + resolve({ + expiresIn: json.expires_in, + accessToken: json.access_token, + refreshToken: json.refresh_token + }); + } else { + reject(new Error('Unable to login.')); + } + }); + }); + + post.write(postData); + + post.end(); + post.on('error', err => { + reject(err); + }); + + } catch (e) { + reject(e); + } + }); + } + + private async refreshToken(refreshToken: string): Promise { + return new Promise((resolve: (value: IToken) => void, reject) => { + const postData = querystring.stringify({ + refresh_token: refreshToken, + client_id: clientId, + grant_type: 'refresh_token', + scope: scope + }); + + const post = https.request({ + host: 'login.microsoftonline.com', + path: `/${tenant}/oauth2/v2.0/token`, + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': postData.length + } + }, result => { + const buffer: Buffer[] = []; + result.on('data', (chunk: Buffer) => { + buffer.push(chunk); + }); + result.on('end', () => { + if (result.statusCode === 200) { + const json = JSON.parse(Buffer.concat(buffer).toString()); + const token = { + expiresIn: json.expires_in, + accessToken: json.access_token, + refreshToken: json.refresh_token + }; + this.setToken(token); + resolve(token); + } else { + vscode.window.showInformationMessage(`error`); + reject(new Error('Bad!')); + } + }); + }); + + post.write(postData); + + post.end(); + post.on('error', err => { + reject(err); + }); + }); + } + + public async logout() { + delete this._token; + await keychain.deleteToken(); + if (this._refreshTimeout) { + clearTimeout(this._refreshTimeout); + } + } +} diff --git a/src/vs/platform/auth/electron-browser/authServer.ts b/extensions/vscode-account/src/authServer.ts similarity index 81% rename from src/vs/platform/auth/electron-browser/authServer.ts rename to extensions/vscode-account/src/authServer.ts index 57bd4fdfb5f..b78803cec16 100644 --- a/src/vs/platform/auth/electron-browser/authServer.ts +++ b/extensions/vscode-account/src/authServer.ts @@ -7,14 +7,46 @@ import * as http from 'http'; 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'; +import * as path from 'path'; interface Deferred { resolve: (result: T | Promise) => void; reject: (reason: any) => void; } +const _typeof = { + number: 'number', + string: 'string', + undefined: 'undefined', + object: 'object', + function: 'function' +}; + +/** + * @returns whether the provided parameter is undefined. + */ +export function isUndefined(obj: any): obj is undefined { + return typeof (obj) === _typeof.undefined; +} + +/** + * @returns whether the provided parameter is undefined or null. + */ +export function isUndefinedOrNull(obj: any): obj is undefined | null { + return isUndefined(obj) || obj === null; +} + +/** + * Asserts that the argument passed in is neither undefined nor null. + */ +export function assertIsDefined(arg: T | null | undefined): T { + if (isUndefinedOrNull(arg)) { + throw new Error('Assertion Failed: argument is undefined or null'); + } + + return arg; +} + export function createTerminateServer(server: http.Server) { const sockets: Record = {}; let socketCount = 0; @@ -140,10 +172,10 @@ export function createServer(nonce: string) { } break; case '/': - sendFile(res, getPathFromAmdModule(require, '../common/auth.html'), 'text/html; charset=utf-8'); + sendFile(res, path.join(__dirname, '../media/auth.html'), 'text/html; charset=utf-8'); break; case '/auth.css': - sendFile(res, getPathFromAmdModule(require, '../common/auth.css'), 'text/css; charset=utf-8'); + sendFile(res, path.join(__dirname, '../media/auth.css'), 'text/css; charset=utf-8'); break; case '/callback': deferredCode.resolve(callback(nonce, reqUrl) diff --git a/extensions/vscode-account/src/extension.ts b/extensions/vscode-account/src/extension.ts new file mode 100644 index 00000000000..53793dc4c43 --- /dev/null +++ b/extensions/vscode-account/src/extension.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { AzureActiveDirectoryService, onDidChangeAccounts } from './AADHelper'; + +export async function activate(context: vscode.ExtensionContext) { + + const loginService = new AzureActiveDirectoryService(); + + await loginService.initialize(); + + vscode.authentication.registerAuthenticationProvider({ + id: 'MSA', + displayName: 'Microsoft Account', // TODO localize + onDidChangeAccounts: onDidChangeAccounts.event, + accounts: loginService.accounts, + login: async () => { + try { + await loginService.login(); + return loginService.accounts[0]!; + } catch (e) { + vscode.window.showErrorMessage(`Logging in failed: ${e}`); + throw e; + } + }, + logout: async (id: string) => { + return loginService.logout(); + } + }); + + return; +} + +// this method is called when your extension is deactivated +export function deactivate() { } diff --git a/extensions/vscode-account/src/keychain.ts b/extensions/vscode-account/src/keychain.ts new file mode 100644 index 00000000000..a53d848c76c --- /dev/null +++ b/extensions/vscode-account/src/keychain.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// keytar depends on a native module shipped in vscode, so this is +// how we load it +import * as keytarType from 'keytar'; + +function getKeytar(): Keytar | undefined { + try { + return require('keytar'); + } catch (err) { + console.log(err); + } + + return undefined; +} + +export type Keytar = { + getPassword: typeof keytarType['getPassword']; + setPassword: typeof keytarType['setPassword']; + deletePassword: typeof keytarType['deletePassword']; +}; + +const SERVICE_ID = 'vscode.login'; +const ACCOUNT_ID = 'account'; + +export class Keychain { + private keytar: Keytar; + + constructor() { + const keytar = getKeytar(); + if (!keytar) { + throw new Error('System keychain unavailable'); + } + + this.keytar = keytar; + } + + async setToken(token: string): Promise { + try { + return await this.keytar.setPassword(SERVICE_ID, ACCOUNT_ID, token); + } catch (e) { + // Ignore + } + } + + async getToken() { + try { + return await this.keytar.getPassword(SERVICE_ID, ACCOUNT_ID); + } catch (e) { + // Ignore + } + } + + async deleteToken() { + try { + return await this.keytar.deletePassword(SERVICE_ID, ACCOUNT_ID); + } catch (e) { + // Ignore + } + } +} + +export const keychain = new Keychain(); diff --git a/extensions/vscode-account/src/utils.ts b/extensions/vscode-account/src/utils.ts new file mode 100644 index 00000000000..164f2236221 --- /dev/null +++ b/extensions/vscode-account/src/utils.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function toBase64UrlEncoding(base64string: string) { + return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // Need to use base64url encoding +} diff --git a/extensions/vscode-account/src/vscode.proposed.d.ts b/extensions/vscode-account/src/vscode.proposed.d.ts new file mode 100644 index 00000000000..0f4a53fee1a --- /dev/null +++ b/extensions/vscode-account/src/vscode.proposed.d.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * This is the place for API experiments and proposals. + * These API are NOT stable and subject to change. They are only available in the Insiders + * distribution and CANNOT be used in published extensions. + * + * To test these API in local environment: + * - Use Insiders release of VS Code. + * - Add `"enableProposedApi": true` to your package.json. + * - Copy this file to your project. + */ + +declare module 'vscode' { + + export interface Account { + readonly id: string; + readonly accessToken: string; + readonly displayName: string; + } + + export interface AuthenticationProvider { + readonly id: string; + readonly displayName: string; + + readonly accounts: ReadonlyArray; + readonly onDidChangeAccounts: Event>; + + login(): Promise; + logout(accountId: string): Promise; + } + + export namespace authentication { + export function registerAuthenticationProvider(provider: AuthenticationProvider): Disposable; + } + + // #region Ben - extension auth flow (desktop+web) + + export namespace env { + + export function asExternalUri(target: Uri): Thenable + } +} diff --git a/extensions/vscode-account/tsconfig.json b/extensions/vscode-account/tsconfig.json new file mode 100644 index 00000000000..46be6dc9581 --- /dev/null +++ b/extensions/vscode-account/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": "out", + "lib": [ + "es6", + "es2016", + "dom" + ], + "typeRoots": [ + "node_modules/@types", + "src/typings" + ], + "sourceMap": true, + "rootDir": "src", + "strict": true, + "noImplicitAny": true + }, + "exclude": [ + "node_modules", + ".vscode-test" + ] +} diff --git a/extensions/vscode-account/yarn.lock b/extensions/vscode-account/yarn.lock new file mode 100644 index 00000000000..4fc295de4b9 --- /dev/null +++ b/extensions/vscode-account/yarn.lock @@ -0,0 +1,658 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" + integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== + dependencies: + "@babel/highlight" "^7.8.3" + +"@babel/highlight@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" + integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@types/keytar@^4.0.1": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@types/keytar/-/keytar-4.4.2.tgz#49ef917d6cbb4f19241c0ab50cd35097b5729b32" + integrity sha512-xtQcDj9ruGnMwvSu1E2BH4SFa5Dv2PvSPd0CKEBLN5hEj/v5YpXJY+B6hAfuKIbvEomD7vJTc/P1s1xPNh2kRw== + dependencies: + keytar "*" + +"@types/node@^10.12.21": + version "10.17.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" + integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== + +"@types/vscode@^1.41.0": + version "1.41.0" + resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.41.0.tgz#b0d75920220f84e07093285e59180c0f11d336cd" + integrity sha512-7SfeY5u9jgiELwxyLB3z7l6l/GbN9CqpCQGkcRlB7tKRFBxzbz2PoBfGrLxI1vRfUCIq5+hg5vtDHExwq5j3+A== + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +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= + +bl@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88" + integrity sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A== + dependencies: + readable-stream "^3.0.1" + +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" + +builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= + +chalk@^2.0.0, chalk@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chownr@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" + integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +commander@^2.12.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +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= + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +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= + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +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= + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= + +glob@^7.1.1: + 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" + +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= + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +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, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +keytar@*: + version "5.0.0" + resolved "https://registry.yarnpkg.com/keytar/-/keytar-5.0.0.tgz#c89b6b7a4608fd7af633d9f8474b1a7eb97cbe6f" + integrity sha512-a5UheK59YOlJf9i+2Osaj/kkH6mK0RCHVMtJ84u6ZfbfRIbOJ/H4b5VlOF/LgNHF6s78dRSBzZnvIuPiBKv6wg== + dependencies: + nan "2.14.0" + prebuild-install "5.3.3" + +mimic-response@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.0.0.tgz#996a51c60adf12cb8a87d7fb8ef24c2f3d5ebb46" + integrity sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ== + +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= + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +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" + +nan@2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +napi-build-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.1.tgz#1381a0f92c39d66bf19852e7873432fc2123e508" + integrity sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA== + +node-abi@^2.7.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.13.0.tgz#e2f2ec444d0aca3ea1b3874b6de41d1665828f63" + integrity sha512-9HrZGFVTR5SOu3PZAnAY2hLO36aW1wmA+FDsVkr85BTST32TLCA1H/AEcatVRAsWLyXS3bqUDYCAjq5/QGuSTA== + dependencies: + semver "^5.4.1" + +noop-logger@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" + integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= + +npmlog@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +once@^1.3.0, once@^1.3.1, once@^1.4.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= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +prebuild-install@5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.3.tgz#ef4052baac60d465f5ba6bf003c9c1de79b9da8e" + integrity sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g== + dependencies: + detect-libc "^1.0.3" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.0" + mkdirp "^0.5.1" + napi-build-utils "^1.0.1" + node-abi "^2.7.0" + noop-logger "^0.1.1" + npmlog "^4.0.1" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^3.0.3" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + which-pm-runs "^1.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@^2.0.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.1, readable-stream@^3.1.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" + integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +resolve@^1.3.2: + version "1.14.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" + integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== + dependencies: + path-parse "^1.0.6" + +safe-buffer@^5.0.1, safe-buffer@~5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +semver@^5.3.0, semver@^5.4.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +simple-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" + integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= + +simple-get@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3" + integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +tar-fs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad" + integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA== + dependencies: + chownr "^1.1.1" + mkdirp "^0.5.1" + pump "^3.0.0" + tar-stream "^2.0.0" + +tar-stream@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3" + integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw== + dependencies: + bl "^3.0.0" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tslib@^1.8.0, tslib@^1.8.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + +tslint@^5.12.1: + version "5.20.1" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" + integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== + dependencies: + "@babel/code-frame" "^7.0.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^4.0.1" + glob "^7.1.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + mkdirp "^0.5.1" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.29.0" + +tsutils@^2.29.0: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.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" + +typescript@^3.7.4: + version "3.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.4.tgz#1743a5ec5fef6a1fa9f3e4708e33c81c73876c19" + integrity sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +which-pm-runs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" + integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +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/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index ee7fab8ba62..4db9464573e 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -50,20 +50,18 @@ import { IFileService } from 'vs/platform/files/common/files'; import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; -import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; +import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel, UserDataAuthTokenServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; -import { IAuthTokenService } from 'vs/platform/auth/common/auth'; -import { AuthTokenService } from 'vs/platform/auth/electron-browser/authTokenService'; -import { AuthTokenChannel } from 'vs/platform/auth/common/authTokenIpc'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; import { UserDataAutoSync } from 'vs/platform/userDataSync/electron-browser/userDataAutoSync'; import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; +import { UserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataAuthTokenService'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -183,7 +181,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); services.set(ICredentialsService, new SyncDescriptor(KeytarCredentialsService)); - services.set(IAuthTokenService, new SyncDescriptor(AuthTokenService)); + services.set(IUserDataAuthTokenService, new SyncDescriptor(UserDataAuthTokenService)); services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService)); services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', activeWindowRouter))); services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService)); @@ -207,8 +205,8 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const diagnosticsChannel = new DiagnosticsChannel(diagnosticsService); server.registerChannel('diagnostics', diagnosticsChannel); - const authTokenService = accessor.get(IAuthTokenService); - const authTokenChannel = new AuthTokenChannel(authTokenService); + const authTokenService = accessor.get(IUserDataAuthTokenService); + const authTokenChannel = new UserDataAuthTokenServiceChannel(authTokenService); server.registerChannel('authToken', authTokenChannel); const settingsSyncService = accessor.get(ISettingsSyncService); diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 66f65392bd6..52c783c9117 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1280,6 +1280,25 @@ export interface RenameProvider { resolveRenameLocation?(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } +/** + * @internal + */ +export interface Account { + id: string; + accessToken: string; + displayName: string; +} + +/** + * @internal + */ +export interface AuthenticationProvider { + getAccount(): Promise; + onDidChangeAccount: Event; + login(): Promise; + logout(accountId: string): Promise; +} + export interface Command { id: string; diff --git a/src/vs/platform/auth/common/auth.ts b/src/vs/platform/auth/common/auth.ts deleted file mode 100644 index 81af0bbf0a4..00000000000 --- a/src/vs/platform/auth/common/auth.ts +++ /dev/null @@ -1,31 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event, Emitter } from 'vs/base/common/event'; -import { URI } from 'vs/base/common/uri'; - -export const enum AuthTokenStatus { - Initializing = 'Initializing', - SignedOut = 'SignedOut', - SignedIn = 'SignedIn', - SigningIn = 'SigningIn', - RefreshingToken = 'RefreshingToken' -} - -export const IAuthTokenService = createDecorator('IAuthTokenService'); - -export interface IAuthTokenService { - _serviceBrand: undefined; - - readonly status: AuthTokenStatus; - readonly onDidChangeStatus: Event; - readonly _onDidGetCallback: Emitter; - - getToken(): Promise; - refreshToken(): Promise; - login(): Promise; - logout(): Promise; -} diff --git a/src/vs/platform/auth/common/authTokenIpc.ts b/src/vs/platform/auth/common/authTokenIpc.ts deleted file mode 100644 index eff088c1114..00000000000 --- a/src/vs/platform/auth/common/authTokenIpc.ts +++ /dev/null @@ -1,31 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Event } from 'vs/base/common/event'; -import { IAuthTokenService } from 'vs/platform/auth/common/auth'; - -export class AuthTokenChannel implements IServerChannel { - - constructor(private readonly service: IAuthTokenService) { } - - listen(_: unknown, event: string): Event { - switch (event) { - case 'onDidChangeStatus': return this.service.onDidChangeStatus; - } - throw new Error(`Event not found: ${event}`); - } - - call(context: any, command: string, args?: any): Promise { - switch (command) { - case '_getInitialStatus': return Promise.resolve(this.service.status); - case 'getToken': return this.service.getToken(); - case 'refreshToken': return this.service.refreshToken(); - case 'login': return this.service.login(); - case 'logout': return this.service.logout(); - } - throw new Error('Invalid call'); - } -} diff --git a/src/vs/platform/auth/electron-browser/authTokenService.ts b/src/vs/platform/auth/electron-browser/authTokenService.ts deleted file mode 100644 index 971e8f7a967..00000000000 --- a/src/vs/platform/auth/electron-browser/authTokenService.ts +++ /dev/null @@ -1,276 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as crypto from 'crypto'; -import * as https from 'https'; -import { Event, Emitter } from 'vs/base/common/event'; -import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; -import { generateUuid } from 'vs/base/common/uuid'; -import { shell } from 'electron'; -import { createServer, startServer } from 'vs/platform/auth/electron-browser/authServer'; -import { IProductService } from 'vs/platform/product/common/productService'; - -const SERVICE_NAME = 'VS Code'; -const ACCOUNT = 'MyAccount'; - -const activeDirectoryResourceId = 'https://management.core.windows.net/'; - -function toQuery(obj: any): string { - return Object.keys(obj).map(key => `${key}=${obj[key]}`).join('&'); -} - -function toBase64UrlEncoding(base64string: string) { - return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // Need to use base64url encoding -} - -export interface IToken { - expiresIn: string; // How long access token is valid, in seconds - expiresOn: string; // When the access token expires in epoch time - accessToken: string; - refreshToken: string; -} - -export class AuthTokenService extends Disposable implements IAuthTokenService { - _serviceBrand: undefined; - - private _status: AuthTokenStatus = AuthTokenStatus.Initializing; - get status(): AuthTokenStatus { return this._status; } - private _onDidChangeStatus: Emitter = this._register(new Emitter()); - readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; - - public readonly _onDidGetCallback: Emitter = this._register(new Emitter()); - readonly onDidGetCallback: Event = this._onDidGetCallback.event; - - private _activeToken: IToken | undefined; - - constructor( - @ICredentialsService private readonly credentialsService: ICredentialsService, - @IProductService private readonly productService: IProductService - ) { - super(); - if (!this.productService.auth) { - return; - } - - this.credentialsService.getPassword(SERVICE_NAME, ACCOUNT).then(storedRefreshToken => { - if (storedRefreshToken) { - this.refresh(storedRefreshToken); - } else { - this.setStatus(AuthTokenStatus.SignedOut); - } - }); - } - - public async login(): Promise { - if (!this.productService.auth) { - throw new Error('Authentication is not configured.'); - } - - this.setStatus(AuthTokenStatus.SigningIn); - - const nonce = generateUuid(); - const { server, redirectPromise, codePromise } = createServer(nonce); - - try { - const port = await startServer(server); - shell.openExternal(`http://localhost:${port}/signin?nonce=${encodeURIComponent(nonce)}`); - - const redirectReq = await redirectPromise; - if ('err' in redirectReq) { - const { err, res } = redirectReq; - res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unkown error')}` }); - res.end(); - throw err; - } - - const host = redirectReq.req.headers.host || ''; - const updatedPortStr = (/^[^:]+:(\d+)$/.exec(Array.isArray(host) ? host[0] : host) || [])[1]; - const updatedPort = updatedPortStr ? parseInt(updatedPortStr, 10) : port; - - const state = `${updatedPort},${encodeURIComponent(nonce)}`; - - const codeVerifier = toBase64UrlEncoding(crypto.randomBytes(32).toString('base64')); - const codeChallenge = toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64')); - - let uri = URI.parse(this.productService.auth.loginUrl); - uri = uri.with({ - query: `response_type=code&client_id=${encodeURIComponent(this.productService.auth.clientId)}&redirect_uri=${this.productService.auth.redirectUrl}&state=${encodeURIComponent(state)}&resource=${activeDirectoryResourceId}&prompt=select_account&code_challenge_method=S256&code_challenge=${codeChallenge}` - }); - - await redirectReq.res.writeHead(302, { Location: uri.toString(true) }); - redirectReq.res.end(); - - const codeRes = await codePromise; - const res = codeRes.res; - - try { - if ('err' in codeRes) { - throw codeRes.err; - } - const token = await this.exchangeCodeForToken(codeRes.code, codeVerifier); - this.setToken(token); - res.writeHead(302, { Location: '/' }); - res.end(); - } catch (err) { - res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unkown error')}` }); - res.end(); - } - } finally { - setTimeout(() => { - server.close(); - }, 5000); - } - - } - - public getToken(): Promise { - return Promise.resolve(this._activeToken?.accessToken); - } - - public async refreshToken(): Promise { - if (!this._activeToken) { - throw new Error('No token to refresh'); - } - - this.refresh(this._activeToken.refreshToken); - } - - private setToken(token: IToken) { - this._activeToken = token; - this.credentialsService.setPassword(SERVICE_NAME, ACCOUNT, token.refreshToken); - this.setStatus(AuthTokenStatus.SignedIn); - } - - private exchangeCodeForToken(code: string, codeVerifier: string): Promise { - return new Promise((resolve: (value: IToken) => void, reject) => { - try { - if (!this.productService.auth) { - throw new Error('Authentication is not configured.'); - } - - const postData = toQuery({ - grant_type: 'authorization_code', - code: code, - client_id: this.productService.auth?.clientId, - code_verifier: codeVerifier, - redirect_uri: this.productService.auth?.redirectUrl - }); - - const tokenUrl = URI.parse(this.productService.auth.tokenUrl); - - const post = https.request({ - host: tokenUrl.authority, - path: tokenUrl.path, - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': postData.length - } - }, result => { - const buffer: Buffer[] = []; - result.on('data', (chunk: Buffer) => { - buffer.push(chunk); - }); - result.on('end', () => { - if (result.statusCode === 200) { - const json = JSON.parse(Buffer.concat(buffer).toString()); - resolve({ - expiresIn: json.access_token, - expiresOn: json.expires_on, - accessToken: json.access_token, - refreshToken: json.refresh_token - }); - } else { - reject(new Error('Bad!')); - } - }); - }); - - post.write(postData); - - post.end(); - post.on('error', err => { - reject(err); - }); - - } catch (e) { - reject(e); - } - }); - } - - private async refresh(refreshToken: string): Promise { - return new Promise((resolve, reject) => { - if (!this.productService.auth) { - throw new Error('Authentication is not configured.'); - } - - this.setStatus(AuthTokenStatus.RefreshingToken); - const postData = toQuery({ - refresh_token: refreshToken, - client_id: this.productService.auth?.clientId, - grant_type: 'refresh_token', - resource: activeDirectoryResourceId - }); - - const tokenUrl = URI.parse(this.productService.auth.tokenUrl); - - const post = https.request({ - host: tokenUrl.authority, - path: tokenUrl.path, - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': postData.length - } - }, result => { - const buffer: Buffer[] = []; - result.on('data', (chunk: Buffer) => { - buffer.push(chunk); - }); - result.on('end', () => { - if (result.statusCode === 200) { - const json = JSON.parse(Buffer.concat(buffer).toString()); - this.setToken({ - expiresIn: json.access_token, - expiresOn: json.expires_on, - accessToken: json.access_token, - refreshToken: json.refresh_token - }); - resolve(); - } else { - reject(new Error('Refreshing token failed.')); - } - }); - }); - - post.write(postData); - - post.end(); - post.on('error', err => { - this.setStatus(AuthTokenStatus.SignedOut); - reject(err); - }); - }); - } - - async logout(): Promise { - await this.credentialsService.deletePassword(SERVICE_NAME, ACCOUNT); - this._activeToken = undefined; - this.setStatus(AuthTokenStatus.SignedOut); - } - - private setStatus(status: AuthTokenStatus): void { - if (this._status !== status) { - this._status = status; - this._onDidChangeStatus.fire(status); - } - } - -} - diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index 1c37427fd5d..120fd666444 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -102,13 +102,6 @@ export interface IProductConfiguration { readonly msftInternalDomains?: string[]; readonly linkProtectionTrustedDomains?: readonly string[]; - - readonly auth?: { - loginUrl: string; - tokenUrl: string; - redirectUrl: string; - clientId: string; - }; } export interface IExeBasedExtensionTip { diff --git a/src/vs/platform/userDataSync/common/userDataAuthTokenService.ts b/src/vs/platform/userDataSync/common/userDataAuthTokenService.ts new file mode 100644 index 00000000000..03ba0c45dfe --- /dev/null +++ b/src/vs/platform/userDataSync/common/userDataAuthTokenService.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; + +export class UserDataAuthTokenService extends Disposable implements IUserDataAuthTokenService { + + _serviceBrand: any; + + private _onDidChangeToken: Emitter = this._register(new Emitter()); + readonly onDidChangeToken: Event = this._onDidChangeToken.event; + + private _token: string | undefined; + + constructor() { + super(); + } + + async getToken(): Promise { + return this._token; + } + + async setToken(token: string | undefined): Promise { + if (token !== this._token) { + this._token = token; + this._onDidChangeToken.fire(token); + } + } +} diff --git a/src/vs/platform/userDataSync/common/userDataAutoSync.ts b/src/vs/platform/userDataSync/common/userDataAutoSync.ts index 75123459141..e6a3d612dee 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSync.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSync.ts @@ -3,12 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, SyncStatus, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { Event } from 'vs/base/common/event'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { timeout } from 'vs/base/common/async'; -import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; +import { Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IUserDataSyncLogService, IUserDataSyncService, SyncStatus, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; export class UserDataAutoSync extends Disposable { @@ -18,16 +17,17 @@ export class UserDataAutoSync extends Disposable { @IConfigurationService private readonly configurationService: IConfigurationService, @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, - @IAuthTokenService private readonly authTokenService: IAuthTokenService, + @IUserDataAuthTokenService private readonly userDataAuthTokenService: IUserDataAuthTokenService, ) { super(); this.updateEnablement(false); - this._register(Event.any(authTokenService.onDidChangeStatus, userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true))); + this._register(Event.any(userDataAuthTokenService.onDidChangeToken)(() => this.updateEnablement(true))); + this._register(Event.any(userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true))); this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('sync.enable'))(() => this.updateEnablement(true))); } - private updateEnablement(stopIfDisabled: boolean): void { - const enabled = this.isSyncEnabled(); + private async updateEnablement(stopIfDisabled: boolean): Promise { + const enabled = await this.isSyncEnabled(); if (this.enabled === enabled) { return; } @@ -60,10 +60,10 @@ export class UserDataAutoSync extends Disposable { } } - private isSyncEnabled(): boolean { + private async isSyncEnabled(): Promise { return this.configurationService.getValue('sync.enable') && this.userDataSyncService.status !== SyncStatus.Uninitialized - && this.authTokenService.status === AuthTokenStatus.SignedIn; + && !!(await this.userDataAuthTokenService.getToken()); } } diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index eacd383b650..71c11fdd645 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -206,6 +206,17 @@ export interface IUserDataSyncUtilService { resolveFormattingOptions(resource: URI): Promise; } +export const IUserDataAuthTokenService = createDecorator('IUserDataAuthTokenService'); + +export interface IUserDataAuthTokenService { + _serviceBrand: undefined; + + readonly onDidChangeToken: Event; + + getToken(): Promise; + setToken(accessToken: string | undefined): Promise; +} + export const IUserDataSyncLogService = createDecorator('IUserDataSyncLogService'); export interface IUserDataSyncLogService extends ILogService { } diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index 61f634471ba..8e647cfef99 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -5,7 +5,7 @@ import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; -import { IUserDataSyncService, IUserDataSyncUtilService, ISettingsSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; import { URI } from 'vs/base/common/uri'; import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; @@ -67,6 +67,25 @@ export class SettingsSyncChannel implements IServerChannel { } } +export class UserDataAuthTokenServiceChannel implements IServerChannel { + constructor(private readonly service: IUserDataAuthTokenService) { } + + listen(_: unknown, event: string): Event { + switch (event) { + case 'onDidChangeToken': return this.service.onDidChangeToken; + } + throw new Error(`Event not found: ${event}`); + } + + call(context: any, command: string, args?: any): Promise { + switch (command) { + case 'setToken': return this.service.setToken(args); + case 'getToken': return this.service.getToken(); + } + throw new Error('Invalid call'); + } +} + export class UserDataSycnUtilServiceChannel implements IServerChannel { constructor(private readonly service: IUserDataSyncUtilService) { } diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 9c4aaf3cb0c..cf1b0d10aae 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -3,14 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource, ISettingsSyncService, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource, ISettingsSyncService, IUserDataSyncLogService, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; import { Emitter, Event } from 'vs/base/common/event'; import { ExtensionsSynchroniser } from 'vs/platform/userDataSync/common/extensionsSync'; import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; import { KeybindingsSynchroniser } from 'vs/platform/userDataSync/common/keybindingsSync'; import { GlobalStateSynchroniser } from 'vs/platform/userDataSync/common/globalStateSync'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -38,9 +37,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ constructor( @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IAuthTokenService private readonly authTokenService: IAuthTokenService, @ISettingsSyncService private readonly settingsSynchroniser: ISettingsSyncService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, + @IUserDataAuthTokenService private readonly userDataAuthTokenService: IUserDataAuthTokenService, ) { super(); this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser)); @@ -51,6 +50,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ if (this.userDataSyncStoreService.userDataSyncStore) { this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeStatus, () => undefined)))(() => this.updateStatus())); + this._register(this.userDataAuthTokenService.onDidChangeToken(e => this.onDidChangeAuthTokenStatus(e))); } this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal)); @@ -60,7 +60,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ if (!this.userDataSyncStoreService.userDataSyncStore) { throw new Error('Not enabled'); } - if (this.authTokenService.status === AuthTokenStatus.SignedOut) { + if (!(await this.userDataAuthTokenService.getToken())) { throw new Error('Not Authenticated. Please sign in to start sync.'); } for (const synchroniser of this.synchronisers) { @@ -76,7 +76,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ if (!this.userDataSyncStoreService.userDataSyncStore) { throw new Error('Not enabled'); } - if (this.authTokenService.status === AuthTokenStatus.SignedOut) { + if (!(await this.userDataAuthTokenService.getToken())) { throw new Error('Not Authenticated. Please sign in to start sync.'); } for (const synchroniser of this.synchronisers) { @@ -92,7 +92,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ if (!this.userDataSyncStoreService.userDataSyncStore) { throw new Error('Not enabled'); } - if (this.authTokenService.status === AuthTokenStatus.SignedOut) { + if (!(await this.userDataAuthTokenService.getToken())) { throw new Error('Not Authenticated. Please sign in to start sync.'); } for (const synchroniser of this.synchronisers) { @@ -120,7 +120,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ if (!this.userDataSyncStoreService.userDataSyncStore) { throw new Error('Not enabled'); } - if (this.authTokenService.status === AuthTokenStatus.SignedOut) { + if (!!(await this.userDataAuthTokenService.getToken())) { throw new Error('Not Authenticated. Please sign in to start sync.'); } for (const synchroniser of this.synchronisers) { @@ -135,7 +135,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ if (!this.userDataSyncStoreService.userDataSyncStore) { throw new Error('Not enabled'); } - if (this.authTokenService.status === AuthTokenStatus.SignedOut) { + if (!!(await this.userDataAuthTokenService.getToken())) { throw new Error('Not Authenticated. Please sign in to start sync.'); } for (const synchroniser of this.synchronisers) { @@ -192,4 +192,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } return SyncSource.UIState; } + + private onDidChangeAuthTokenStatus(token: string | undefined): void { + if (!token) { + this.stop(); + } + } } diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index bc499a7c48a..a77449c0577 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -4,13 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, } from 'vs/base/common/lifecycle'; -import { IUserData, IUserDataSyncStoreService, UserDataSyncStoreErrorCode, UserDataSyncStoreError, IUserDataSyncStore, getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserData, IUserDataSyncStoreService, UserDataSyncStoreErrorCode, UserDataSyncStoreError, IUserDataSyncStore, getUserDataSyncStore, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; import { IRequestService, asText, isSuccess } from 'vs/platform/request/common/request'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IHeaders, IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; -import { IAuthTokenService } from 'vs/platform/auth/common/auth'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class UserDataSyncStoreService extends Disposable implements IUserDataSyncStoreService { @@ -22,7 +21,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn constructor( @IConfigurationService configurationService: IConfigurationService, @IRequestService private readonly requestService: IRequestService, - @IAuthTokenService private readonly authTokenService: IAuthTokenService, + @IUserDataAuthTokenService private readonly authTokenService: IUserDataAuthTokenService, ) { super(); this.userDataSyncStore = getUserDataSyncStore(configurationService); @@ -98,7 +97,6 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn const context = await this.requestService.request(options, token); if (context.res.statusCode === 401) { - this.authTokenService.refreshToken(); // Throw Unauthorized Error throw new UserDataSyncStoreError('Unauthorized', UserDataSyncStoreErrorCode.Unauthroized); } diff --git a/src/vs/platform/userDataSync/electron-browser/userDataAutoSync.ts b/src/vs/platform/userDataSync/electron-browser/userDataAutoSync.ts index 90ec9533ecd..821d8a05ed5 100644 --- a/src/vs/platform/userDataSync/electron-browser/userDataAutoSync.ts +++ b/src/vs/platform/userDataSync/electron-browser/userDataAutoSync.ts @@ -3,12 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncLogService, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; import { Event } from 'vs/base/common/event'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { UserDataAutoSync as BaseUserDataAutoSync } from 'vs/platform/userDataSync/common/userDataAutoSync'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IAuthTokenService } from 'vs/platform/auth/common/auth'; export class UserDataAutoSync extends BaseUserDataAutoSync { @@ -17,7 +16,7 @@ export class UserDataAutoSync extends BaseUserDataAutoSync { @IElectronService electronService: IElectronService, @IConfigurationService configurationService: IConfigurationService, @IUserDataSyncLogService logService: IUserDataSyncLogService, - @IAuthTokenService authTokenService: IAuthTokenService, + @IUserDataAuthTokenService authTokenService: IUserDataAuthTokenService, ) { super(configurationService, userDataSyncService, logService, authTokenService); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 27a17629b13..be480f96639 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -16,7 +16,28 @@ declare module 'vscode' { - //#region Alex - resolvers, AlexR - ports + export interface Account { + readonly id: string; + readonly accessToken: string; + readonly displayName: string; + } + + export interface AuthenticationProvider { + readonly id: string; + readonly displayName: string; + + readonly accounts: ReadonlyArray; + readonly onDidChangeAccounts: Event>; + + login(): Promise; + logout(accountId: string): Promise; + } + + export namespace authentication { + export function registerAuthenticationProvider(provider: AuthenticationProvider): Disposable; + } + + //#region Alex - resolvers export interface RemoteAuthorityResolverContext { resolveAttempt: number; diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 5560678f607..d77e37c35d8 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -59,6 +59,7 @@ import './mainThreadComments'; import './mainThreadTask'; import './mainThreadLabelService'; import './mainThreadTunnelService'; +import './mainThreadAuthentication'; import 'vs/workbench/api/common/apiCommands'; export class ExtensionPoints implements IWorkbenchContribution { diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts new file mode 100644 index 00000000000..c9fbc6e3e50 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import * as modes from 'vs/editor/common/modes'; +import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol'; + +export class MainThreadAuthenticationProvider { + public readonly handle: number; + constructor( + private readonly _proxy: ExtHostAuthenticationShape, + public readonly id: string, + handle: number + ) { + this.handle = handle; + } + + accounts(): Promise> { + return this._proxy.$accounts(this.handle); + } + + login(): Promise { + return this._proxy.$login(this.handle); + } + + logout(accountId: string): Promise { + return this._proxy.$logout(this.handle, accountId); + } +} + +@extHostNamedCustomer(MainContext.MainThreadAuthentication) +export class MainThreadAuthentication extends Disposable implements MainThreadAuthenticationShape { + private readonly _proxy: ExtHostAuthenticationShape; + private _handlers = new Map(); + + constructor( + extHostContext: IExtHostContext, + @IAuthenticationService private readonly authenticationService: IAuthenticationService, + ) { + super(); + this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication); + } + + $registerAuthenticationProvider(handle: number, id: string): void { + const provider = new MainThreadAuthenticationProvider(this._proxy, id, handle); + this._handlers.set(handle, id); + this.authenticationService.registerAuthenticationProvider(id, provider); + } + + $unregisterAuthenticationProvider(handle: number): void { + const id = this._handlers.get(handle); + if (!id) { + throw new Error(`No authentication provider registered with id ${id}`); + } + + this.authenticationService.unregisterAuthenticationProvider(id); + } + + $onDidChangeAccounts(handle: number, accounts: ReadonlyArray) { + const id = this._handlers.get(handle); + if (id) { + this.authenticationService.accountsUpdate(id, accounts); + } + } +} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index d38277ed35b..0438ad43777 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -69,6 +69,7 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming'; import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; +import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -128,6 +129,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress))); const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHostLabelService, new ExtHostLabelService(rpcProtocol)); const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol)); + const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); // Check that no named customers are missing const expected: ProxyIdentifier[] = values(ExtHostContext); @@ -175,6 +177,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }; })(); + const authentication: typeof vscode.authentication = { + registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable { + return extHostAuthentication.registerAuthenticationProvider(provider); + } + }; // namespace: commands const commands: typeof vscode.commands = { @@ -830,6 +837,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return { version: initData.version, // namespaces + authentication, commands, debug, env, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 227a885f876..12081788f04 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -147,6 +147,12 @@ export interface MainThreadCommentsShape extends IDisposable { $onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent): void; } +export interface MainThreadAuthenticationShape extends IDisposable { + $registerAuthenticationProvider(handle: number, id: string): void; + $unregisterAuthenticationProvider(handle: number): void; + $onDidChangeAccounts(handle: number, accounts: ReadonlyArray): void; +} + export interface MainThreadConfigurationShape extends IDisposable { $updateConfigurationOption(target: ConfigurationTarget | null, key: string, value: any, overrides: IConfigurationOverrides | undefined, scopeToLanguage: boolean | undefined): Promise; $removeConfigurationOption(target: ConfigurationTarget | null, key: string, overrides: IConfigurationOverrides | undefined, scopeToLanguage: boolean | undefined): Promise; @@ -891,6 +897,13 @@ export interface ExtHostLabelServiceShape { $registerResourceLabelFormatter(formatter: ResourceLabelFormatter): IDisposable; } +export interface ExtHostAuthenticationShape { + $accounts(handle: number): Promise>; + $login(handle: number): Promise; + $logout(handle: number, accountId: string): Promise; + // TODO rmacfarlane +} + export interface ExtHostSearchShape { $provideFileSearchResults(handle: number, session: number, query: search.IRawQuery, token: CancellationToken): Promise; $provideTextSearchResults(handle: number, session: number, query: search.IRawTextQuery, token: CancellationToken): Promise; @@ -1412,6 +1425,7 @@ export interface ExtHostTunnelServiceShape { // --- proxy identifiers export const MainContext = { + MainThreadAuthentication: createMainId('MainThreadAuthentication'), MainThreadClipboard: createMainId('MainThreadClipboard'), MainThreadCommands: createMainId('MainThreadCommands'), MainThreadComments: createMainId('MainThreadComments'), @@ -1487,5 +1501,6 @@ export const ExtHostContext = { ExtHostOutputService: createMainId('ExtHostOutputService'), ExtHostLabelService: createMainId('ExtHostLabelService'), ExtHostTheming: createMainId('ExtHostTheming'), - ExtHostTunnelService: createMainId('ExtHostTunnelService') + ExtHostTunnelService: createMainId('ExtHostTunnelService'), + ExtHostAuthentication: createMainId('ExtHostAuthentication') }; diff --git a/src/vs/workbench/api/common/extHostAuthentication.ts b/src/vs/workbench/api/common/extHostAuthentication.ts new file mode 100644 index 00000000000..1ab42296c86 --- /dev/null +++ b/src/vs/workbench/api/common/extHostAuthentication.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * 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 modes from 'vs/editor/common/modes'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IMainContext, MainContext, MainThreadAuthenticationShape, ExtHostAuthenticationShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +export class ExtHostAuthenticationProvider implements IDisposable { + constructor(private _provider: vscode.AuthenticationProvider, + private readonly _handle: number, + private _proxy: MainThreadAuthenticationShape) { + this._provider.onDidChangeAccounts(x => this._proxy.$onDidChangeAccounts(this._handle, this._provider.accounts)); + } + + get accounts(): ReadonlyArray { + return this._provider.accounts; + } + + login(): Promise { + return this._provider.login(); + } + + logout(accountId: string): Promise { + return this._provider.logout(accountId); + } + + dispose(): void { + this._proxy.$unregisterAuthenticationProvider(this._handle); + } +} + +export class ExtHostAuthentication implements ExtHostAuthenticationShape { + public static _handlePool: number = 0; + private _proxy: MainThreadAuthenticationShape; + private _authenticationProviders: Map = new Map(); + + constructor(mainContext: IMainContext) { + this._proxy = mainContext.getProxy(MainContext.MainThreadAuthentication); + } + + private readonly _onDidRefreshToken = new Emitter(); + readonly onDidRefreshToken: Event = this._onDidRefreshToken.event; + + registerAuthenticationProvider(provider: vscode.AuthenticationProvider) { + const handle = ExtHostAuthentication._handlePool++; + const authenticationProvider = new ExtHostAuthenticationProvider(provider, handle, this._proxy); + this._authenticationProviders.set(handle, authenticationProvider); + + this._proxy.$registerAuthenticationProvider(handle, provider.id); + return authenticationProvider; + } + + $accounts(handle: number): Promise> { + const authProvider = this._authenticationProviders.get(handle); + if (authProvider) { + return Promise.resolve(authProvider.accounts); + } + + throw new Error(`Unable to find authentication provider with handle: ${handle}`); + } + + $login(handle: number): Promise { + const authProvider = this._authenticationProviders.get(handle); + if (authProvider) { + return authProvider.login(); + } + + throw new Error(`Unable to find authentication provider with handle: ${handle}`); + } + + $logout(handle: number, accountId: string): Promise { + const authProvider = this._authenticationProviders.get(handle); + if (authProvider) { + return authProvider.logout(accountId); + } + + throw new Error(`Unable to find authentication provider with handle: ${handle}`); + } +} diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSync.ts index d0d54c3c403..31ec8de3166 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSync.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncLogService, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IAuthTokenService } from 'vs/platform/auth/common/auth'; import { Event } from 'vs/base/common/event'; import { UserDataAutoSync as BaseUserDataAutoSync } from 'vs/platform/userDataSync/common/userDataAutoSync'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -18,7 +17,7 @@ export class UserDataAutoSync extends BaseUserDataAutoSync { @IUserDataSyncService userDataSyncService: IUserDataSyncService, @IConfigurationService configurationService: IConfigurationService, @IUserDataSyncLogService logService: IUserDataSyncLogService, - @IAuthTokenService authTokenService: IAuthTokenService, + @IUserDataAuthTokenService authTokenService: IUserDataAuthTokenService, @IInstantiationService instantiationService: IInstantiationService, @IHostService hostService: IHostService, ) { diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 222643aa7b4..5d87233cb66 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, IUserDataSyncStore, registerConfiguration, getUserDataSyncStore, ISyncConfiguration } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, IUserDataSyncStore, registerConfiguration, getUserDataSyncStore, ISyncConfiguration, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; import { localize } from 'vs/nls'; import { Disposable, MutableDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -24,9 +24,7 @@ import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { isEqual } from 'vs/base/common/resources'; import { IEditorInput } from 'vs/workbench/common/editor'; -import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { FalseContext } from 'vs/platform/contextkey/common/contextkeys'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { isWeb } from 'vs/base/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -35,25 +33,35 @@ import { UserDataSyncTrigger } from 'vs/workbench/contrib/userDataSync/browser/u import { timeout } from 'vs/base/common/async'; import { IOutputService } from 'vs/workbench/contrib/output/common/output'; import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; +import { IAuthenticationService, ChangeAccountEventData } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { Account } from 'vs/editor/common/modes'; -const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey('authTokenStatus', AuthTokenStatus.Initializing); +const enum MSAAuthStatus { + Initializing = 'Initializing', + SignedIn = 'SignedIn', + SignedOut = 'SignedOut' +} +const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey('authTokenStatus', MSAAuthStatus.Initializing); const SYNC_PUSH_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-light.svg`)); const SYNC_PUSH_DARK_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-dark.svg`)); +const MSA = 'MSA'; + export class UserDataSyncWorkbenchContribution extends Disposable implements IWorkbenchContribution { private static readonly ENABLEMENT_SETTING = 'sync.enable'; private readonly userDataSyncStore: IUserDataSyncStore | undefined; private readonly syncStatusContext: IContextKey; - private readonly authTokenContext: IContextKey; + private readonly authenticationState: IContextKey; private readonly badgeDisposable = this._register(new MutableDisposable()); private readonly conflictsWarningDisposable = this._register(new MutableDisposable()); private readonly signInNotificationDisposable = this._register(new MutableDisposable()); + private _activeAccount: Account | undefined; constructor( @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, - @IAuthTokenService private readonly authTokenService: IAuthTokenService, + @IAuthenticationService private readonly authenticationService: IAuthenticationService, @IContextKeyService contextKeyService: IContextKeyService, @IActivityService private readonly activityService: IActivityService, @INotificationService private readonly notificationService: INotificationService, @@ -66,45 +74,111 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @IQuickInputService private readonly quickInputService: IQuickInputService, @IInstantiationService instantiationService: IInstantiationService, @IOutputService private readonly outputService: IOutputService, + @IUserDataAuthTokenService private readonly userDataAuthTokenService: IUserDataAuthTokenService, ) { super(); this.userDataSyncStore = getUserDataSyncStore(configurationService); this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService); - this.authTokenContext = CONTEXT_AUTH_TOKEN_STATE.bindTo(contextKeyService); - + this.authenticationState = CONTEXT_AUTH_TOKEN_STATE.bindTo(contextKeyService); if (this.userDataSyncStore) { registerConfiguration(); - this.onDidChangeAuthTokenStatus(this.authTokenService.status); this.onDidChangeSyncStatus(this.userDataSyncService.status); - this._register(Event.debounce(authTokenService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeAuthTokenStatus(this.authTokenService.status))); this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeSyncStatus(this.userDataSyncService.status))); this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING))(() => this.onDidChangeEnablement())); + this._register(this.authenticationService.onDidRegisterAuthenticationProvider(e => this.onDidRegisterAuthenticationProvider(e))); + this._register(this.authenticationService.onDidUnregisterAuthenticationProvider(e => this.onDidUnregisterAuthenticationProvider(e))); + this._register(this.authenticationService.onDidChangeAccounts(e => this.onDidChangeAccounts(e))); this.registerActions(); - - if (isWeb) { - this._register(instantiationService.createInstance(UserDataAutoSync)); - } else { - this._register(instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync(() => this.triggerSync())); - } + this.initializeActiveAccount().then(_ => { + if (isWeb) { + this._register(instantiationService.createInstance(UserDataAutoSync)); + } else { + this._register(instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync(() => this.triggerSync())); + } + }); } } private triggerSync(): void { if (this.configurationService.getValue('sync.enable') && this.userDataSyncService.status !== SyncStatus.Uninitialized - && this.authTokenService.status === AuthTokenStatus.SignedIn) { + && this.authenticationState.get() === MSAAuthStatus.SignedIn) { this.userDataSyncService.sync(); } } - private onDidChangeAuthTokenStatus(status: AuthTokenStatus) { - this.authTokenContext.set(status); - if (status === AuthTokenStatus.SignedIn) { - this.signInNotificationDisposable.clear(); + private async initializeActiveAccount(): Promise { + const accounts = await this.authenticationService.getAccounts(MSA); + // MSA provider has not yet been registered + if (!accounts) { + return; } + + if (accounts.length === 0) { + this.activeAccount = undefined; + return; + } + + if (accounts.length === 1) { + this.activeAccount = accounts[0]; + return; + } + + const selectedAccount = await this.quickInputService.pick(accounts.map(account => { + return { + id: account.id, + label: account.displayName + }; + }), { canPickMany: false }); + + if (selectedAccount) { + this.activeAccount = accounts.filter(account => selectedAccount.id === account.id)[0]; + } + } + + get activeAccount(): Account | undefined { + return this._activeAccount; + } + + set activeAccount(account: Account | undefined) { + this._activeAccount = account; + + if (account) { + this.userDataAuthTokenService.setToken(account.accessToken); + this.authenticationState.set(MSAAuthStatus.SignedIn); + } else { + this.userDataAuthTokenService.setToken(undefined); + this.authenticationState.set(MSAAuthStatus.SignedOut); + } + this.updateBadge(); } + private onDidChangeAccounts(event: ChangeAccountEventData): void { + if (event.providerId === MSA) { + if (this.activeAccount) { + // Try to update existing account, case where access token has been refreshed + const matchingAccount = event.accounts.filter(a => a.id === this.activeAccount?.id)[0]; + this.activeAccount = matchingAccount; + } else { + this.initializeActiveAccount(); + } + } + } + + private async onDidRegisterAuthenticationProvider(providerId: string) { + if (providerId === MSA) { + await this.initializeActiveAccount(); + } + } + + private onDidUnregisterAuthenticationProvider(providerId: string) { + if (providerId === MSA) { + this.activeAccount = undefined; + this.authenticationState.reset(); + } + } + private onDidChangeSyncStatus(status: SyncStatus) { this.syncStatusContext.set(status); @@ -140,7 +214,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this.updateBadge(); const enabled = this.configurationService.getValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING); if (enabled) { - if (this.authTokenService.status === AuthTokenStatus.SignedOut) { + if (this.authenticationState.get() === MSAAuthStatus.SignedOut) { const handle = this.notificationService.prompt(Severity.Info, this.getSignInAndTurnOnDetailString(), [ { @@ -156,19 +230,15 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } } - private updateBadge(): void { + private async updateBadge(): Promise { this.badgeDisposable.clear(); let badge: IBadge | undefined = undefined; let clazz: string | undefined; let priority: number | undefined = undefined; - if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.configurationService.getValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING) && this.authTokenService.status === AuthTokenStatus.SignedOut) { + if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.configurationService.getValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING) && this.authenticationState.get() === MSAAuthStatus.SignedOut) { badge = new NumberBadge(1, () => localize('sign in to sync', "Sign in to Sync")); - } else if (this.authTokenService.status === AuthTokenStatus.SigningIn) { - badge = new ProgressBadge(() => localize('signing in', "Signing in...")); - clazz = 'progress-badge'; - priority = 1; } else if (this.userDataSyncService.status === SyncStatus.HasConflicts) { badge = new NumberBadge(1, () => localize('resolve conflicts', "Resolve Conflicts")); } else if (this.userDataSyncService.status === SyncStatus.Syncing) { @@ -279,7 +349,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private async turnOn(): Promise { const message = localize('turn on sync', "Turn on Sync"); let detail: string, primaryButton: string; - if (this.authTokenService.status === AuthTokenStatus.SignedIn) { + if (this.authenticationState.get() === MSAAuthStatus.SignedIn) { detail = this.getTurnOnDetailString(); primaryButton = localize('turn on', "Turn on"); } else { @@ -291,7 +361,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo case 1: return; case 2: await this.configureSyncOptions(); return this.turnOn(); } - if (this.authTokenService.status !== AuthTokenStatus.SignedIn) { + if (this.authenticationState.get() === MSAAuthStatus.SignedOut) { await this.signIn(); } await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, true); @@ -357,7 +427,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private async signIn(): Promise { try { - await this.authTokenService.login(); + this.activeAccount = await this.authenticationService.login(MSA); } catch (e) { this.notificationService.error(e); throw e; @@ -365,7 +435,10 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } private async signOut(): Promise { - await this.authTokenService.logout(); + if (this.activeAccount) { + await this.authenticationService.logout(MSA, this.activeAccount.id); + this.activeAccount = undefined; + } } private async continueSync(): Promise { @@ -435,7 +508,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private registerActions(): void { const turnOnSyncCommandId = 'workbench.userData.actions.syncStart'; - const turnOnSyncWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.not(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.notEqualsTo(AuthTokenStatus.SigningIn)); + const turnOnSyncWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.not(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.notEqualsTo(MSAAuthStatus.Initializing)); CommandsRegistry.registerCommand(turnOnSyncCommandId, () => this.turnOn()); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '5_sync', @@ -454,7 +527,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); const signInCommandId = 'workbench.userData.actions.signin'; - const signInWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SignedOut)); + const signInWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(MSAAuthStatus.SignedOut)); CommandsRegistry.registerCommand(signInCommandId, () => this.signIn()); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '5_sync', @@ -472,18 +545,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo when: signInWhenContext, }); - const signingInCommandId = 'workbench.userData.actions.signingin'; - CommandsRegistry.registerCommand(signingInCommandId, () => null); - MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { - group: '5_sync', - command: { - id: signingInCommandId, - title: localize('signinig in', "Signing in..."), - precondition: FalseContext - }, - when: CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SigningIn) - }); - const stopSyncCommandId = 'workbench.userData.actions.stopSync'; CommandsRegistry.registerCommand(stopSyncCommandId, () => this.turnOff()); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { @@ -492,7 +553,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo id: stopSyncCommandId, title: localize('global activity stop sync', "Turn off sync") }, - when: ContextKeyExpr.and(ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SignedIn), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.HasConflicts)) + when: ContextKeyExpr.and(ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(MSAAuthStatus.SignedIn), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.HasConflicts)) }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { @@ -563,7 +624,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo id: 'workbench.userData.actions.signout', title: localize('sign out', "Sync: Sign out") }, - when: ContextKeyExpr.and(CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SignedIn)), + when: ContextKeyExpr.and(CONTEXT_AUTH_TOKEN_STATE.isEqualTo(MSAAuthStatus.SignedIn)), }; CommandsRegistry.registerCommand(signOutMenuItem.command.id, () => this.signOut()); MenuRegistry.appendMenuItem(MenuId.CommandPalette, signOutMenuItem); diff --git a/src/vs/workbench/services/authToken/browser/authTokenService.ts b/src/vs/workbench/services/authToken/browser/authTokenService.ts deleted file mode 100644 index ef7af7506a2..00000000000 --- a/src/vs/workbench/services/authToken/browser/authTokenService.ts +++ /dev/null @@ -1,74 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { localize } from 'vs/nls'; -import { Event, Emitter } from 'vs/base/common/event'; -import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { URI } from 'vs/base/common/uri'; - -const SERVICE_NAME = 'VS Code'; -const ACCOUNT = 'MyAccount'; - -export class AuthTokenService extends Disposable implements IAuthTokenService { - _serviceBrand: undefined; - - private _status: AuthTokenStatus = AuthTokenStatus.Initializing; - get status(): AuthTokenStatus { return this._status; } - private _onDidChangeStatus: Emitter = this._register(new Emitter()); - readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; - - readonly _onDidGetCallback: Emitter = this._register(new Emitter()); - - constructor( - @ICredentialsService private readonly credentialsService: ICredentialsService, - @IQuickInputService private readonly quickInputService: IQuickInputService - ) { - super(); - this.getToken().then(token => { - if (token) { - this.setStatus(AuthTokenStatus.SignedIn); - } else { - this.setStatus(AuthTokenStatus.SignedOut); - } - }); - } - - async getToken(): Promise { - const token = await this.credentialsService.getPassword(SERVICE_NAME, ACCOUNT); - if (token) { - return token; - } - - return; - } - - async login(): Promise { - const token = await this.quickInputService.input({ placeHolder: localize('enter token', "Please provide the auth bearer token"), ignoreFocusLost: true, }); - if (token) { - await this.credentialsService.setPassword(SERVICE_NAME, ACCOUNT, token); - this.setStatus(AuthTokenStatus.SignedIn); - } - } - - async refreshToken(): Promise { - await this.logout(); - } - - async logout(): Promise { - await this.credentialsService.deletePassword(SERVICE_NAME, ACCOUNT); - this.setStatus(AuthTokenStatus.SignedOut); - } - - private setStatus(status: AuthTokenStatus): void { - if (this._status !== status) { - this._status = status; - this._onDidChangeStatus.fire(status); - } - } - -} diff --git a/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts b/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts deleted file mode 100644 index ad7abd58826..00000000000 --- a/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts +++ /dev/null @@ -1,61 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { Emitter, Event } from 'vs/base/common/event'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; -import { URI } from 'vs/base/common/uri'; - -export class AuthTokenService extends Disposable implements IAuthTokenService { - - _serviceBrand: undefined; - - private readonly channel: IChannel; - - private _status: AuthTokenStatus = AuthTokenStatus.Initializing; - get status(): AuthTokenStatus { return this._status; } - private _onDidChangeStatus: Emitter = this._register(new Emitter()); - readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; - - readonly _onDidGetCallback: Emitter = this._register(new Emitter()); - - constructor( - @ISharedProcessService sharedProcessService: ISharedProcessService, - ) { - super(); - this.channel = sharedProcessService.getChannel('authToken'); - this._register(this.channel.listen('onDidChangeStatus')(status => this.updateStatus(status))); - this.channel.call('_getInitialStatus').then(status => this.updateStatus(status)); - } - - getToken(): Promise { - return this.channel.call('getToken'); - } - - login(): Promise { - return this.channel.call('login'); - } - - refreshToken(): Promise { - return this.channel.call('getToken'); - } - - logout(): Promise { - return this.channel.call('logout'); - } - - private async updateStatus(status: AuthTokenStatus): Promise { - if (status !== AuthTokenStatus.Initializing) { - this._status = status; - this._onDidChangeStatus.fire(status); - } - } - -} - -registerSingleton(IAuthTokenService, AuthTokenService); diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts new file mode 100644 index 00000000000..ca2a2fd8e1a --- /dev/null +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -0,0 +1,96 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Account } from 'vs/editor/common/modes'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { MainThreadAuthenticationProvider } from 'vs/workbench/api/browser/mainThreadAuthentication'; + +export const IAuthenticationService = createDecorator('IAuthenticationService'); + +export interface ChangeAccountEventData { + providerId: string; + accounts: ReadonlyArray; +} + +export interface IAuthenticationService { + _serviceBrand: undefined; + + registerAuthenticationProvider(id: string, provider: MainThreadAuthenticationProvider): void; + unregisterAuthenticationProvider(id: string): void; + accountsUpdate(providerId: string, accounts: ReadonlyArray): void; + + readonly onDidRegisterAuthenticationProvider: Event; + readonly onDidUnregisterAuthenticationProvider: Event; + + readonly onDidChangeAccounts: Event; + getAccounts(providerId: string): Promise | undefined>; + login(providerId: string): Promise; + logout(providerId: string, accountId: string): Promise; +} + +export class AuthenticationService extends Disposable implements IAuthenticationService { + _serviceBrand: undefined; + + private _authenticationProviders: Map = new Map(); + + private _onDidRegisterAuthenticationProvider: Emitter = this._register(new Emitter()); + readonly onDidRegisterAuthenticationProvider: Event = this._onDidRegisterAuthenticationProvider.event; + + private _onDidUnregisterAuthenticationProvider: Emitter = this._register(new Emitter()); + readonly onDidUnregisterAuthenticationProvider: Event = this._onDidUnregisterAuthenticationProvider.event; + + private _onDidChangeAccounts: Emitter = this._register(new Emitter()); + readonly onDidChangeAccounts: Event = this._onDidChangeAccounts.event; + + constructor() { + super(); + } + + registerAuthenticationProvider(id: string, authenticationProvider: MainThreadAuthenticationProvider): void { + this._authenticationProviders.set(id, authenticationProvider); + this._onDidRegisterAuthenticationProvider.fire(id); + } + + unregisterAuthenticationProvider(id: string): void { + this._authenticationProviders.delete(id); + this._onDidUnregisterAuthenticationProvider.fire(id); + } + + accountsUpdate(providerId: string, accounts: ReadonlyArray): void { + this._onDidChangeAccounts.fire({ providerId, accounts }); + } + + async getAccounts(id: string): Promise | undefined> { + const authProvider = this._authenticationProviders.get(id); + if (authProvider) { + return await authProvider.accounts(); + } + + return undefined; + } + + async login(id: string): Promise { + const authProvider = this._authenticationProviders.get(id); + if (authProvider) { + return authProvider.login(); + } else { + throw new Error(`No authentication provider '${id}' is currently registered.`); + } + } + + async logout(id: string, accountId: string): Promise { + const authProvider = this._authenticationProviders.get(id); + if (authProvider) { + return authProvider.logout(accountId); + } else { + throw new Error(`No authentication provider '${id}' is currently registered.`); + } + } +} + +registerSingleton(IAuthenticationService, AuthenticationService); diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataAuthTokenService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataAuthTokenService.ts new file mode 100644 index 00000000000..998630e1ef9 --- /dev/null +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataAuthTokenService.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; + +export class UserDataAuthTokenService extends Disposable implements IUserDataAuthTokenService { + + _serviceBrand: undefined; + + private readonly channel: IChannel; + private _onDidChangeToken: Emitter = this._register(new Emitter()); + readonly onDidChangeToken: Event = this._onDidChangeToken.event; + + constructor( + @ISharedProcessService sharedProcessService: ISharedProcessService, + ) { + super(); + this.channel = sharedProcessService.getChannel('authToken'); + } + + getToken(): Promise { + return this.channel.call('getToken'); + } + + setToken(token: string | undefined): Promise { + return this.channel.call('setToken', token); + } +} + +registerSingleton(IUserDataAuthTokenService, UserDataAuthTokenService); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index bd8b76dad8f..f9656900129 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -51,7 +51,8 @@ import 'vs/workbench/services/workspaces/electron-browser/workspacesService'; import 'vs/workbench/services/workspaces/electron-browser/workspaceEditingService'; import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncService'; import 'vs/workbench/services/userDataSync/electron-browser/settingsSyncService'; -import 'vs/workbench/services/authToken/electron-browser/authTokenService'; +import 'vs/workbench/services/userDataSync/electron-browser/userDataAuthTokenService'; +import 'vs/workbench/services/authentication/browser/authenticationService'; import 'vs/workbench/services/host/electron-browser/desktopHostService'; import 'vs/workbench/services/request/electron-browser/requestService'; import 'vs/workbench/services/lifecycle/electron-browser/lifecycleService'; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index e6c3807eeff..9db377b87dd 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -63,9 +63,8 @@ import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { TunnelService } from 'vs/workbench/services/remote/common/tunnelService'; import { ILoggerService } from 'vs/platform/log/common/log'; import { FileLoggerService } from 'vs/platform/log/common/fileLogService'; -import { IAuthTokenService } from 'vs/platform/auth/common/auth'; -import { AuthTokenService } from 'vs/workbench/services/authToken/browser/authTokenService'; import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService, ISettingsSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { AuthenticationService, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; @@ -77,7 +76,7 @@ registerSingleton(IAccessibilityService, BrowserAccessibilityService, true); registerSingleton(IContextMenuService, ContextMenuService); registerSingleton(ITunnelService, TunnelService, true); registerSingleton(ILoggerService, FileLoggerService); -registerSingleton(IAuthTokenService, AuthTokenService); +registerSingleton(IAuthenticationService, AuthenticationService); registerSingleton(IUserDataSyncLogService, UserDataSyncLogService); registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService); registerSingleton(ISettingsSyncService, SettingsSynchroniser); From 3883ebd0615109548afc3c0b4d5fcca8dc701a03 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 14 Jan 2020 15:27:42 -0800 Subject: [PATCH 289/843] Add common service for logging deprecated api usage (#88585) * Add common service for logging deprecated api usage For #88391 Adds a new `ExtHostApiDeprecationService`. This service logs a warning and telemetry when a deprecated API is used. Updates some of the more simple deprecated apis to use this new service * Note that extensionId cannot be undefined * Fix event name --- .../workbench/api/common/extHost.api.impl.ts | 23 ++++-- .../common/extHostApiDeprecationService.ts | 71 +++++++++++++++++++ .../api/common/extHostLanguageFeatures.ts | 47 ++++++++---- src/vs/workbench/api/node/extHost.services.ts | 2 + .../extensions/worker/extHost.services.ts | 2 + .../api/extHostApiCommands.test.ts | 3 +- .../api/extHostLanguageFeatures.test.ts | 3 +- 7 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 src/vs/workbench/api/common/extHostApiDeprecationService.ts diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 0438ad43777..1cc6c7e6108 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -69,6 +69,7 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming'; import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; +import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication'; export interface IExtensionApiFactory { @@ -90,6 +91,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostStorage = accessor.get(IExtHostStorage); const extHostLogService = accessor.get(ILogService); const extHostTunnelService = accessor.get(IExtHostTunnelService); + const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService); // register addressable instances rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService); @@ -119,7 +121,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService)); const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment)); const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol, extHostLogService)); - const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService)); + const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService, extHostApiDeprecation)); const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures)); const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostLogService, extHostDocumentsAndEditors)); const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands)); @@ -388,7 +390,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostLanguageFeatures.registerCallHierarchyProvider(extension, selector, provider); }, setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => { - return extHostLanguageFeatures.setLanguageConfiguration(language, configuration); + return extHostLanguageFeatures.setLanguageConfiguration(extension, language, configuration); } }; @@ -508,6 +510,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable); }, withScmProgress(task: (progress: vscode.Progress) => Thenable) { + extHostApiDeprecation.report('window.withScmProgress', extension, + `Use 'withProgress' instead.`); + return extHostProgress.withProgress(extension, { location: extHostTypes.ProgressLocation.SourceControl }, (progress, token) => task({ report(n: number) { /*noop*/ } })); }, withProgress(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; worked?: number }>, token: vscode.CancellationToken) => Thenable) { @@ -569,13 +574,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }; // namespace: workspace - let warnedRootPathDeprecated = false; + const workspace: typeof vscode.workspace = { get rootPath() { - if (extension.isUnderDevelopment && !warnedRootPathDeprecated) { - warnedRootPathDeprecated = true; - extHostLogService.warn(`[Deprecation Warning] 'workspace.rootPath' is deprecated and should no longer be used. Please use 'workspace.workspaceFolders' instead. More details: https://aka.ms/vscode-eliminating-rootpath`); - } + extHostApiDeprecation.report('workspace.rootPath', extension, + `Please use 'workspace.workspaceFolders' instead. More details: https://aka.ms/vscode-eliminating-rootpath`); return extHostWorkspace.getPath(); }, @@ -689,6 +692,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostDocumentContentProviders.registerTextDocumentContentProvider(scheme, provider); }, registerTaskProvider: (type: string, provider: vscode.TaskProvider) => { + extHostApiDeprecation.report('window.registerTaskProvider', extension, + `Use the corresponding function on the 'tasks' namespace instead`); + return extHostTask.registerTaskProvider(extension, type, provider); }, registerFileSystemProvider(scheme, provider, options) { @@ -740,6 +746,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // namespace: scm const scm: typeof vscode.scm = { get inputBox() { + extHostApiDeprecation.report('scm.inputBox', extension, + `Use 'SourceControl.inputBox' instead`); + return extHostSCM.getLastInputBox(extension)!; // Strict null override - Deprecated api }, createSourceControl(id: string, label: string, rootUri?: vscode.Uri) { diff --git a/src/vs/workbench/api/common/extHostApiDeprecationService.ts b/src/vs/workbench/api/common/extHostApiDeprecationService.ts new file mode 100644 index 00000000000..010458267f0 --- /dev/null +++ b/src/vs/workbench/api/common/extHostApiDeprecationService.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; +import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; + +export interface IExtHostApiDeprecationService { + _serviceBrand: undefined; + + report(apiId: string, extension: IExtensionDescription, migrationSuggestion: string): void; +} + +export const IExtHostApiDeprecationService = createDecorator('IExtHostApiDeprecationService'); + +export class ExtHostApiDeprecationService implements IExtHostApiDeprecationService { + + _serviceBrand: undefined; + + private readonly _reportedUsages = new Set(); + private readonly _telemetryShape: extHostProtocol.MainThreadTelemetryShape; + + constructor( + @IExtHostRpcService rpc: IExtHostRpcService, + @ILogService private readonly _extHostLogService: ILogService, + ) { + this._telemetryShape = rpc.getProxy(extHostProtocol.MainContext.MainThreadTelemetry); + } + + public report(apiId: string, extension: IExtensionDescription, migrationSuggestion: string): void { + const key = this.getUsageKey(apiId, extension); + if (this._reportedUsages.has(key)) { + return; + } + this._reportedUsages.add(key); + + if (extension.isUnderDevelopment) { + this._extHostLogService.warn(`[Deprecation Warning] '${apiId}' is deprecated. ${migrationSuggestion}`); + } + + type DeprecationTelemetry = { + extensionId: string; + apiId: string; + }; + type DeprecationTelemetryMeta = { + extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + apiId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + }; + this._telemetryShape.$publicLog2('extHostDeprecatedApiUsage', { + extensionId: extension.identifier.value, + apiId: apiId, + }); + } + + private getUsageKey(apiId: string, extension: IExtensionDescription): string { + return `${apiId}-${extension.identifier.value}`; + } +} + + +export const NullApiDeprecationService = Object.freeze(new class implements IExtHostApiDeprecationService { + _serviceBrand: undefined; + + public report(_apiId: string, _extension: IExtensionDescription, _warningMessage: string): void { + // noop + } +}()); diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index adfbebe622a..c440c5a3f14 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -29,6 +29,7 @@ import { DisposableStore, dispose } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; import { encodeSemanticTokensDto } from 'vs/workbench/api/common/shared/semanticTokens'; import { IdGenerator } from 'vs/base/common/idGenerator'; +import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; // --- adapter @@ -326,7 +327,8 @@ class CodeActionAdapter { private readonly _diagnostics: ExtHostDiagnostics, private readonly _provider: vscode.CodeActionProvider, private readonly _logService: ILogService, - private readonly _extensionId: ExtensionIdentifier + private readonly _extension: IExtensionDescription, + private readonly _apiDeprecation: IExtHostApiDeprecationService, ) { } provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise { @@ -367,6 +369,10 @@ class CodeActionAdapter { } if (CodeActionAdapter._isCommand(candidate)) { // old school: synthetic code action + + this._apiDeprecation.report('CodeActionProvider.provideCodeActions - return commands', this._extension, + `Return 'CodeAction' instances instead.`); + actions.push({ _isSynthetic: true, title: candidate.title, @@ -375,9 +381,9 @@ class CodeActionAdapter { } else { if (codeActionContext.only) { if (!candidate.kind) { - this._logService.warn(`${this._extensionId.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`); + this._logService.warn(`${this._extension.identifier.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`); } else if (!codeActionContext.only.contains(candidate.kind)) { - this._logService.warn(`${this._extensionId.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`); + this._logService.warn(`${this._extension.identifier.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`); } } @@ -748,8 +754,9 @@ class SuggestAdapter { private readonly _commands: CommandsConverter, private readonly _provider: vscode.CompletionItemProvider, private readonly _logService: ILogService, + private readonly _apiDeprecation: IExtHostApiDeprecationService, private readonly _telemetry: extHostProtocol.MainThreadTelemetryShape, - private readonly _extensionId: ExtensionIdentifier + private readonly _extension: IExtensionDescription, ) { } provideCompletionItems(resource: URI, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise { @@ -838,15 +845,15 @@ class SuggestAdapter { let _mustNotChangeIndex = !this._didWarnMust && SuggestAdapter._mustNotChangeDiff(_mustNotChange, resolvedItem); if (typeof _mustNotChangeIndex === 'string') { - this._logService.warn(`[${this._extensionId.value}] INVALID result from 'resolveCompletionItem', extension MUST NOT change any of: label, sortText, filterText, insertText, or textEdit`); - this._telemetry.$publicLog2('resolveCompletionItem/invalid', { extensionId: this._extensionId.value, kind: 'must', index: _mustNotChangeIndex }); + this._logService.warn(`[${this._extension.identifier.value}] INVALID result from 'resolveCompletionItem', extension MUST NOT change any of: label, sortText, filterText, insertText, or textEdit`); + this._telemetry.$publicLog2('resolveCompletionItem/invalid', { extensionId: this._extension.identifier.value, kind: 'must', index: _mustNotChangeIndex }); this._didWarnMust = true; } let _mayNotChangeIndex = !this._didWarnShould && SuggestAdapter._mayNotChangeDiff(_mayNotChange, resolvedItem); if (typeof _mayNotChangeIndex === 'string') { - this._logService.info(`[${this._extensionId.value}] UNSAVE result from 'resolveCompletionItem', extension SHOULD NOT change any of: additionalTextEdits, or command`); - this._telemetry.$publicLog2('resolveCompletionItem/invalid', { extensionId: this._extensionId.value, kind: 'should', index: _mayNotChangeIndex }); + this._logService.info(`[${this._extension.identifier.value}] UNSAVE result from 'resolveCompletionItem', extension SHOULD NOT change any of: additionalTextEdits, or command`); + this._telemetry.$publicLog2('resolveCompletionItem/invalid', { extensionId: this._extension.identifier.value, kind: 'should', index: _mayNotChangeIndex }); this._didWarnShould = true; } @@ -892,6 +899,9 @@ class SuggestAdapter { // 'insertText'-logic if (item.textEdit) { + this._apiDeprecation.report('CompletionItem.textEdit', this._extension, + `Use 'CompletionItem.insertText' and 'CompletionItem.range' instead.`); + result[extHostProtocol.ISuggestDataDtoField.insertText] = item.textEdit.newText; } else if (typeof item.insertText === 'string') { @@ -1339,6 +1349,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF private _diagnostics: ExtHostDiagnostics; private _adapter = new Map(); private readonly _logService: ILogService; + private readonly _apiDeprecation: IExtHostApiDeprecationService; constructor( mainContext: extHostProtocol.IMainContext, @@ -1346,7 +1357,8 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF documents: ExtHostDocuments, commands: ExtHostCommands, diagnostics: ExtHostDiagnostics, - logService: ILogService + logService: ILogService, + apiDeprecationService: IExtHostApiDeprecationService, ) { this._uriTransformer = uriTransformer; this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadLanguageFeatures); @@ -1355,6 +1367,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF this._commands = commands; this._diagnostics = diagnostics; this._logService = logService; + this._apiDeprecation = apiDeprecationService; } private _transformDocumentSelector(selector: vscode.DocumentSelector): Array { @@ -1562,7 +1575,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- quick fix registerCodeActionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { - const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension.identifier), extension); + const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension, this._apiDeprecation), extension); this._proxy.$registerQuickFixSupport(handle, this._transformDocumentSelector(selector), (metadata && metadata.providedCodeActionKinds) ? metadata.providedCodeActionKinds.map(kind => kind.value) : undefined); return this._createDisposable(handle); } @@ -1665,7 +1678,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- suggestion registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable { - const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._logService, this._telemetryShape, extension.identifier), extension); + const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._logService, this._apiDeprecation, this._telemetryShape, extension), extension); this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider), extension.identifier); return this._createDisposable(handle); } @@ -1813,7 +1826,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return onEnterRules.map(ExtHostLanguageFeatures._serializeOnEnterRule); } - setLanguageConfiguration(languageId: string, configuration: vscode.LanguageConfiguration): vscode.Disposable { + setLanguageConfiguration(extension: IExtensionDescription, languageId: string, configuration: vscode.LanguageConfiguration): vscode.Disposable { let { wordPattern } = configuration; // check for a valid word pattern @@ -1828,6 +1841,16 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF this._documents.setWordDefinitionFor(languageId, undefined); } + if (configuration.__electricCharacterSupport) { + this._apiDeprecation.report('LanguageConfiguration.__electricCharacterSupport', extension, + `Do not use.`); + } + + if (configuration.__characterPairSupport) { + this._apiDeprecation.report('LanguageConfiguration.__characterPairSupport', extension, + `Do not use.`); + } + const handle = this._nextHandle(); const serializedConfiguration: extHostProtocol.ILanguageConfigurationDto = { comments: configuration.comments, diff --git a/src/vs/workbench/api/node/extHost.services.ts b/src/vs/workbench/api/node/extHost.services.ts index 326a1c9cb8d..72ad75d63ef 100644 --- a/src/vs/workbench/api/node/extHost.services.ts +++ b/src/vs/workbench/api/node/extHost.services.ts @@ -28,9 +28,11 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; import { ExtHostTunnelService } from 'vs/workbench/api/node/extHostTunnelService'; +import { IExtHostApiDeprecationService, ExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; // register singleton services registerSingleton(ILogService, ExtHostLogService); +registerSingleton(IExtHostApiDeprecationService, ExtHostApiDeprecationService); registerSingleton(IExtHostOutputService, ExtHostOutputService2); registerSingleton(IExtHostWorkspace, ExtHostWorkspace); registerSingleton(IExtHostDecorations, ExtHostDecorations); diff --git a/src/vs/workbench/services/extensions/worker/extHost.services.ts b/src/vs/workbench/services/extensions/worker/extHost.services.ts index 168fd087270..bbb72e9511c 100644 --- a/src/vs/workbench/services/extensions/worker/extHost.services.ts +++ b/src/vs/workbench/services/extensions/worker/extHost.services.ts @@ -22,9 +22,11 @@ import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiatio import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostLogService } from 'vs/workbench/api/worker/extHostLogService'; import { IExtHostTunnelService, ExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; +import { IExtHostApiDeprecationService, ExtHostApiDeprecationService, } from 'vs/workbench/api/common/extHostApiDeprecationService'; // register singleton services registerSingleton(ILogService, ExtHostLogService); +registerSingleton(IExtHostApiDeprecationService, ExtHostApiDeprecationService); registerSingleton(IExtHostOutputService, ExtHostOutputService); registerSingleton(IExtHostWorkspace, ExtHostWorkspace); registerSingleton(IExtHostDecorations, ExtHostDecorations); 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 06138aaba40..3142b84cc1a 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -32,6 +32,7 @@ import { nullExtensionDescription } from 'vs/workbench/services/extensions/commo import { dispose } from 'vs/base/common/lifecycle'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; +import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = EditorModel.createFromString( @@ -122,7 +123,7 @@ suite('ExtHostLanguageFeatureCommands', function () { const diagnostics = new ExtHostDiagnostics(rpcProtocol, new NullLogService()); rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, diagnostics, new NullLogService()); + extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, diagnostics, new NullLogService(), NullApiDeprecationService); rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, rpcProtocol)); diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index b4b04e1cc83..0208284f9cc 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -47,6 +47,7 @@ import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { dispose } from 'vs/base/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = EditorModel.createFromString( @@ -105,7 +106,7 @@ suite('ExtHostLanguageFeatures', function () { const diagnostics = new ExtHostDiagnostics(rpcProtocol, new NullLogService()); rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, diagnostics, new NullLogService()); + extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, diagnostics, new NullLogService(), NullApiDeprecationService); rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, rpcProtocol)); From ffcc6b92e1899f2633536826e1a4b225ab55a152 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 01:35:38 +0100 Subject: [PATCH 290/843] :lipstick: --- src/vs/platform/userDataSync/common/extensionsSync.ts | 2 +- .../platform/userDataSync/common/globalStateSync.ts | 2 +- .../platform/userDataSync/common/keybindingsSync.ts | 11 ++++++++--- src/vs/platform/userDataSync/common/settingsSync.ts | 6 +++++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index d8d83228bbd..d97262ec02c 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -122,7 +122,7 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser const { added, removed, updated, remote } = merge(localExtensions, null, null, [], this.getIgnoredExtensions()); await this.apply({ added, removed, updated, remote, remoteUserData: null, skippedExtensions: [] }); - this.logService.info('Extensions: Finished pulling extensions.'); + this.logService.info('Extensions: Finished pushing extensions.'); } finally { this.setStatus(SyncStatus.Idle); } diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 36c81f2e038..1baefb1c24e 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -104,7 +104,7 @@ export class GlobalStateSynchroniser extends Disposable implements ISynchroniser const remote = await this.getLocalGlobalState(); await this.apply({ local: undefined, remote, remoteUserData: null }); - this.logService.info('UI State: Finished pulling UI State.'); + this.logService.info('UI State: Finished pushing UI State.'); } finally { this.setStatus(SyncStatus.Idle); } diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index bea6e5671c8..3167c18320f 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -89,9 +89,10 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser const remoteContent = remoteUserData.content !== null ? this.getKeybindingsContentFromSyncContent(remoteUserData.content) : null; if (remoteContent !== null) { - await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(remoteContent)); + await this.fileService.writeFile(this.environmentService.keybindingsSyncPreviewResource, VSBuffer.fromString(remoteContent)); + const fileContent = await this.getLocalFileContent(); this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve({ - fileContent: null, + fileContent, hasConflicts: false, hasLocalChanged: true, hasRemoteChanged: false, @@ -104,6 +105,8 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser else { this.logService.info('Keybindings: Remote keybindings does not exist.'); } + + this.logService.info('Keybindings: Finished pulling keybindings.'); } finally { this.setStatus(SyncStatus.Idle); } @@ -140,6 +143,8 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser else { this.logService.info('Keybindings: Local keybindings does not exist.'); } + + this.logService.info('Keybindings: Finished pushing keybindings.'); } finally { this.setStatus(SyncStatus.Idle); } @@ -173,6 +178,7 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser return false; } await this.apply(); + this.logService.trace('Keybindings: Finished synchronizing keybindings...'); return true; } catch (e) { this.syncPreviewResultPromise = null; @@ -263,7 +269,6 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser this.logService.trace('Keybindings: No changes found during synchronizing keybindings.'); } - this.logService.trace('Keybindings: Finised synchronizing keybindings.'); this.syncPreviewResultPromise = null; } diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 813826aa728..203743d2b7c 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -122,6 +122,8 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer else { this.logService.info('Settings: Remote settings does not exist.'); } + + this.logService.info('Settings: Finished pulling settings.'); } finally { this.setStatus(SyncStatus.Idle); } @@ -162,6 +164,8 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer else { this.logService.info('Settings: Local settings does not exist.'); } + + this.logService.info('Settings: Finished pushing settings.'); } finally { this.setStatus(SyncStatus.Idle); } @@ -225,6 +229,7 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer return false; } await this.apply(); + this.logService.trace('Settings: Finished synchronizing settings.'); return true; } catch (e) { this.syncPreviewResultPromise = null; @@ -293,7 +298,6 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer this.logService.trace('Settings: No changes found during synchronizing settings.'); } - this.logService.trace('Settings: Finised synchronizing settings.'); this.syncPreviewResultPromise = null; } From f1ad679258c02ef403dc773852cac40999c59db8 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 01:38:09 +0100 Subject: [PATCH 291/843] disable caching of sync requests --- src/vs/platform/userDataSync/common/userDataSyncStoreService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index a77449c0577..df5094c426b 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -34,6 +34,8 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', key, 'latest').toString(); const headers: IHeaders = {}; + // Disable caching as they are cached by synchronisers + headers['Cache-Control'] = 'no-cache'; if (oldValue) { headers['If-None-Match'] = oldValue.ref; } From bbc2da76a9798c2f8a562af56ac41c5a420a7e37 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 01:51:35 +0100 Subject: [PATCH 292/843] fix token check --- .../userDataSync/browser/userDataSync.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 5d87233cb66..0f7b0c92f7c 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -364,6 +364,8 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (this.authenticationState.get() === MSAAuthStatus.SignedOut) { await this.signIn(); } + + await this.handleFirstTimeSync(); await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, true); this.notificationService.info(localize('Sync Started', "Sync Started.")); } @@ -413,6 +415,34 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } + private async handleFirstTimeSync(): Promise { + + const hasRemote = await this.userDataSyncService.hasRemote(); + const hasPreviouslySynced = await this.userDataSyncService.hasPreviouslySynced(); + + if (hasRemote && !hasPreviouslySynced) { + const result = await this.dialogService.show( + Severity.Info, + localize('firs time sync', "First time synchronization"), + [ + localize('continue', "Continue"), + localize('cancel', "Cancel"), + localize('download', "Download"), + localize('upload', "Upload"), + ], + { + cancelId: 1, + detail: localize('first time sync detail', "Synchronising from this device for the first time. Would you like to \nDownload and replace with the remote data or \nUpload from this device and replace the remote data?") + } + ); + + switch (result.choice) { + case 2: return this.userDataSyncService.pull(); + case 3: return this.userDataSyncService.push(); + } + } + } + private async turnOff(): Promise { const result = await this.dialogService.confirm({ type: 'info', From cc2d1a64443063d9116a5b68baf794fcecad013f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 01:57:05 +0100 Subject: [PATCH 293/843] Revert "fix token check" This reverts commit bbc2da76a9798c2f8a562af56ac41c5a420a7e37. --- .../userDataSync/browser/userDataSync.ts | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 0f7b0c92f7c..5d87233cb66 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -364,8 +364,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (this.authenticationState.get() === MSAAuthStatus.SignedOut) { await this.signIn(); } - - await this.handleFirstTimeSync(); await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, true); this.notificationService.info(localize('Sync Started', "Sync Started.")); } @@ -415,34 +413,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } - private async handleFirstTimeSync(): Promise { - - const hasRemote = await this.userDataSyncService.hasRemote(); - const hasPreviouslySynced = await this.userDataSyncService.hasPreviouslySynced(); - - if (hasRemote && !hasPreviouslySynced) { - const result = await this.dialogService.show( - Severity.Info, - localize('firs time sync', "First time synchronization"), - [ - localize('continue', "Continue"), - localize('cancel', "Cancel"), - localize('download', "Download"), - localize('upload', "Upload"), - ], - { - cancelId: 1, - detail: localize('first time sync detail', "Synchronising from this device for the first time. Would you like to \nDownload and replace with the remote data or \nUpload from this device and replace the remote data?") - } - ); - - switch (result.choice) { - case 2: return this.userDataSyncService.pull(); - case 3: return this.userDataSyncService.push(); - } - } - } - private async turnOff(): Promise { const result = await this.dialogService.confirm({ type: 'info', From 18653ef66af71e12650409a025e78849ee9a3d64 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 01:57:52 +0100 Subject: [PATCH 294/843] fix token check --- src/vs/platform/userDataSync/common/userDataSyncService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index cf1b0d10aae..84102d532bd 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -120,7 +120,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ if (!this.userDataSyncStoreService.userDataSyncStore) { throw new Error('Not enabled'); } - if (!!(await this.userDataAuthTokenService.getToken())) { + if (!(await this.userDataAuthTokenService.getToken())) { throw new Error('Not Authenticated. Please sign in to start sync.'); } for (const synchroniser of this.synchronisers) { @@ -135,7 +135,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ if (!this.userDataSyncStoreService.userDataSyncStore) { throw new Error('Not enabled'); } - if (!!(await this.userDataAuthTokenService.getToken())) { + if (!(await this.userDataAuthTokenService.getToken())) { throw new Error('Not Authenticated. Please sign in to start sync.'); } for (const synchroniser of this.synchronisers) { From 17faa988790062ab85cd58c3fbfe209137a28d7c Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 14 Jan 2020 10:00:20 -0800 Subject: [PATCH 295/843] WIP on search editors backed by files --- .../contrib/search/browser/searchEditor.ts | 11 ++- .../search/browser/searchEditorCommands.ts | 91 +++++++++++++------ 2 files changed, 72 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchEditor.ts b/src/vs/workbench/contrib/search/browser/searchEditor.ts index 26bc97814d1..055a5540426 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditor.ts @@ -35,6 +35,7 @@ import { SearchModel } from 'vs/workbench/contrib/search/common/searchModel'; import { IPatternInfo, ISearchConfigurationProperties, ITextQuery } from 'vs/workbench/services/search/common/search'; import { Delayer } from 'vs/base/common/async'; import { serializeSearchResultForEditor, SearchConfiguration, SearchEditorInput } from 'vs/workbench/contrib/search/browser/searchEditorCommands'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; const RESULT_LINE_REGEX = /^(\s+)(\d+)(:| )(\s+)(.*)$/; @@ -70,6 +71,7 @@ export class SearchEditor extends BaseEditor { @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextViewService private readonly contextViewService: IContextViewService, @ICommandService private readonly commandService: ICommandService, + @ITextFileService private readonly textFileService: ITextFileService, ) { super(SearchEditor.ID, telemetryService, themeService, storageService); } @@ -268,10 +270,16 @@ export class SearchEditor extends BaseEditor { } async setInput(newInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + await super.setInput(newInput, options, token); + if (!(newInput instanceof SearchEditorInput)) { return; } this.pauseSearching = true; - // TODO: Manage model lifecycle in SearchEditorInput const model = this.modelService.getModel(newInput.resource); + if (newInput.resource.scheme !== 'search-editor') { + if (model?.getValue() === '') { + model.setValue((await this.textFileService.read(newInput.resource)).value); + } + } this.searchResultEditor.setModel(model); this.queryEditorWidget.setValue(newInput.config.query, true); this.queryEditorWidget.searchInput.setCaseSensitive(newInput.config.caseSensitive); @@ -284,7 +292,6 @@ export class SearchEditor extends BaseEditor { this.toggleIncludesExcludes(newInput.config.showIncludesExcludes); this.focusInput(); - await super.setInput(newInput, options, token); this.pauseSearching = false; } diff --git a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts index 985f49c4347..bbcc5ba4d2e 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts @@ -5,7 +5,7 @@ import { coalesce, flatten } from 'vs/base/common/arrays'; import * as network from 'vs/base/common/network'; -import { repeat } from 'vs/base/common/strings'; +import { repeat, endsWith } from 'vs/base/common/strings'; import { assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/searchEditor'; @@ -33,7 +33,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import type { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -// import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; +import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; @@ -50,24 +50,45 @@ export type SearchConfiguration = { }; export class SearchEditorContribution implements IWorkbenchContribution { - // constructor( - // @IEditorService private readonly editorService: IEditorService, - // @ITextFileService protected readonly textFileService: ITextFileService, - // ) { - // this.editorService.overrideOpenEditor(async (editor, options, group) => { - // const resource = editor.getResource(); - // if (!resource || !endsWith(resource.path, '.code-search') || !(editor instanceof FileEditorInput)) { - // return undefined; - // } + constructor( + @IEditorService private readonly editorService: IEditorService, + @ITextFileService protected readonly textFileService: ITextFileService, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + ) { + this.editorService.overrideOpenEditor((editor, options, group) => { + const resource = editor.getResource(); + if (!resource || !endsWith(resource.path, '.code-search') || !(editor instanceof FileEditorInput)) { + return undefined; + } - // console.log('hello agian'); - // const contents = await this.textFileService.read(resource); - // contents.value; + if (group.isOpened(editor)) { + return undefined; + } - // return undefined; + return { + override: (async () => { + const contents = (await this.textFileService.read(resource)).value; + const header = searchHeaderToContentPattern(contents.split('\n').slice(0, 5)); - // }); - // } + const input = instantiationService.createInstance( + SearchEditorInput, + { + query: header.pattern, + regexp: header.flags.regex, + caseSensitive: header.flags.caseSensitive, + wholeWord: header.flags.wholeWord, + includes: header.includes, + excludes: header.excludes, + contextLines: header.context ?? 0, + useIgnores: !header.flags.ignoreExcludes, + showIncludesExcludes: !!(header.includes || header.excludes || header.flags.ignoreExcludes) + }, contents, resource); + + return editorService.openEditor(input, { ...options, pinned: true, ignoreOverrides: true }, group); + })() + }; + }); + } } export class SearchEditorInputFactory implements IEditorInputFactory { @@ -75,11 +96,17 @@ export class SearchEditorInputFactory implements IEditorInputFactory { canSerialize() { return true; } serialize(input: SearchEditorInput) { - return JSON.stringify(input.config); + let resource = undefined; + if (input.resource.path) { + resource = input.resource.toString(); + } + + return JSON.stringify({ ...input.config, resource }); } deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): SearchEditorInput | undefined { - return instantiationService.createInstance(SearchEditorInput, JSON.parse(serializedEditorInput), undefined); + const { resource, ...config } = JSON.parse(serializedEditorInput); + return instantiationService.createInstance(SearchEditorInput, config, undefined, resource && URI.parse(resource)); } } @@ -98,6 +125,7 @@ export class SearchEditorInput extends EditorInput { constructor( config: SearchConfiguration | undefined, initialContents: string | undefined, + resource: URI | undefined, @IModelService private readonly modelService: IModelService, @IModeService private readonly modeService: IModeService, @IEditorService protected readonly editorService: IEditorService, @@ -106,8 +134,7 @@ export class SearchEditorInput extends EditorInput { @IUntitledTextEditorService protected readonly untitledTextEditorService: IUntitledTextEditorService, ) { super(); - this.resource = URI.from({ scheme: 'search-editor', fragment: `${searchEditorInputInstances++}` }); - + this.resource = resource ?? URI.from({ scheme: 'search-editor', fragment: `${searchEditorInputInstances++}` }); if (config === undefined) { this._config = { query: '', includes: '', excludes: '', contextLines: 0, wholeWord: false, caseSensitive: false, regexp: false, useIgnores: true, showIncludesExcludes: false }; @@ -117,14 +144,19 @@ export class SearchEditorInput extends EditorInput { const searchResultMode = this.modeService.create('search-result'); - this.model = this.modelService.createModel(initialContents ?? '', searchResultMode, this.resource); + this.model = this.modelService.getModel(this.resource) ?? this.modelService.createModel(initialContents ?? '', searchResultMode, this.resource); } async save(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { - const path = (this.config.query.replace(/\W/g, '') || 'Untitled Search') + '.code-search'; - const untitledResource = URI.from({ scheme: 'untitled', path, fragment: this.resource.fragment }); - const a = this.untitledTextEditorService.createOrGet(untitledResource, 'search-result', this.model.getValue()); - return a.save(group, options).finally(() => a.dispose()); + if (this.resource.scheme === 'search-editor') { + const path = (this.config.query.replace(/\W/g, '') || 'Untitled Search') + '.code-search'; + const untitledResource = URI.from({ scheme: 'untitled', path, fragment: this.resource.fragment }); + const a = this.untitledTextEditorService.createOrGet(untitledResource, 'search-result', this.model.getValue()); + return a.save(group, options).finally(() => a.dispose()); + } else { + const a = this.untitledTextEditorService.createOrGet(this.resource, 'search-result', this.model.getValue(), undefined, true); + return a.save(group, options).finally(() => a.dispose()); + } } getTypeId(): string { @@ -154,6 +186,9 @@ export class SearchEditorInput extends EditorInput { if (other instanceof UntitledTextEditorInput) { if (other.getResource().fragment === this.resource.fragment) { return true; } } + if (other instanceof SearchEditorInput) { + if (other.resource.path && other.resource.path === this.resource.path) { return true; } + } return false; } } @@ -432,7 +467,7 @@ export const refreshActiveEditorSearch = export const openNewSearchEditor = async (editorService: IEditorService, instantiationService: IInstantiationService) => { - await editorService.openEditor(instantiationService.createInstance(SearchEditorInput, undefined, undefined), { pinned: true }); + await editorService.openEditor(instantiationService.createInstance(SearchEditorInput, undefined, undefined, undefined), { pinned: true }); }; export const createEditorFromSearchResult = @@ -481,7 +516,7 @@ export const createEditorFromSearchResult = contextLines: 0, useIgnores: !searchResult.query.userDisabledExcludesAndIgnoreFiles, showIncludesExcludes: !!(rawExcludePattern || rawExcludePattern || searchResult.query.userDisabledExcludesAndIgnoreFiles) - }, contents); + }, contents, undefined); const editor = await editorService.openEditor(input, { pinned: true }) as SearchEditor; const model = assertIsDefined(editor.getModel()); From b6b3fe55ce606fa268b454adfe1580e4d0976728 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 14 Jan 2020 18:18:15 -0800 Subject: [PATCH 296/843] Ongoing work on save/open search results. --- .../search/browser/searchEditorCommands.ts | 88 +++++++++++++++---- 1 file changed, 73 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts index bbcc5ba4d2e..93f3fc6ea7f 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts @@ -25,7 +25,7 @@ import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/co import { FileMatch, Match, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPatternInfo, ISearchConfigurationProperties, ITextQuery } from 'vs/workbench/services/search/common/search'; -import { IEditorInputFactory, GroupIdentifier, EditorInput } from 'vs/workbench/common/editor'; +import { IEditorInputFactory, GroupIdentifier, EditorInput, SaveContext } from 'vs/workbench/common/editor'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { SearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor'; @@ -34,6 +34,11 @@ import { ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/te import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import type { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; +import { dirname, joinPath, isEqual } from 'vs/base/common/resources'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { basename } from 'vs/base/common/path'; @@ -54,10 +59,14 @@ export class SearchEditorContribution implements IWorkbenchContribution { @IEditorService private readonly editorService: IEditorService, @ITextFileService protected readonly textFileService: ITextFileService, @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IModelService protected readonly modelService: IModelService, ) { + this.editorService.overrideOpenEditor((editor, options, group) => { const resource = editor.getResource(); - if (!resource || !endsWith(resource.path, '.code-search') || !(editor instanceof FileEditorInput)) { + if (!resource || + !(endsWith(resource.path, '.code-search') || resource.scheme === 'search-editor') || + !(editor instanceof FileEditorInput || (resource.scheme === 'search-editor'))) { return undefined; } @@ -67,7 +76,7 @@ export class SearchEditorContribution implements IWorkbenchContribution { return { override: (async () => { - const contents = (await this.textFileService.read(resource)).value; + const contents = resource.scheme === 'search-editor' ? this.modelService.getModel(resource)?.getValue() ?? '' : (await this.textFileService.read(resource)).value; const header = searchHeaderToContentPattern(contents.split('\n').slice(0, 5)); const input = instantiationService.createInstance( @@ -84,7 +93,7 @@ export class SearchEditorContribution implements IWorkbenchContribution { showIncludesExcludes: !!(header.includes || header.excludes || header.flags.ignoreExcludes) }, contents, resource); - return editorService.openEditor(input, { ...options, pinned: true, ignoreOverrides: true }, group); + return editorService.openEditor(input, { ...options, pinned: resource.scheme === 'search-editor', ignoreOverrides: true }, group); })() }; }); @@ -132,6 +141,10 @@ export class SearchEditorInput extends EditorInput { @IEditorGroupsService protected readonly editorGroupService: IEditorGroupsService, @ITextFileService protected readonly textFileService: ITextFileService, @IUntitledTextEditorService protected readonly untitledTextEditorService: IUntitledTextEditorService, + @IHistoryService private readonly historyService: IHistoryService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IFileDialogService private readonly fileDialogService: IFileDialogService, + @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); this.resource = resource ?? URI.from({ scheme: 'search-editor', fragment: `${searchEditorInputInstances++}` }); @@ -149,22 +162,42 @@ export class SearchEditorInput extends EditorInput { async save(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { if (this.resource.scheme === 'search-editor') { - const path = (this.config.query.replace(/\W/g, '') || 'Untitled Search') + '.code-search'; - const untitledResource = URI.from({ scheme: 'untitled', path, fragment: this.resource.fragment }); - const a = this.untitledTextEditorService.createOrGet(untitledResource, 'search-result', this.model.getValue()); - return a.save(group, options).finally(() => a.dispose()); + const path = await this.promptForPath(this.resource, this.suggestFileName()); + if (path) { + if (await this.textFileService.saveAs(this.resource, path, options)) { + if (options?.context !== SaveContext.EDITOR_CLOSE && !isEqual(path, this.resource)) { + const replacement = this.instantiationService.createInstance(SearchEditorInput, this.config, undefined, path); + await this.editorService.replaceEditors([{ editor: this, replacement, options: { pinned: true } }], group); + return true; + } + } + } + return false; } else { - const a = this.untitledTextEditorService.createOrGet(this.resource, 'search-result', this.model.getValue(), undefined, true); - return a.save(group, options).finally(() => a.dispose()); + return !!this.textFileService.write(this.resource, this.model.getValue(), options); } } + // Brining this over from textFileService because it only suggests for untitled scheme. + // In the future I may just use the untitled scheme. I dont get particular benefit from using search-editor... + private async promptForPath(resource: URI, defaultUri: URI): 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({ + defaultUri, + title: localize('saveAsTitle', "Save As"), + }); + } + getTypeId(): string { return SearchEditorInput.ID; } getName(): string { - return this.config.query ? localize('searchTitle.withQuery', "Search: {0}", this.config.query) : localize('searchTitle', "Search"); + if (this.resource.scheme === 'search-editor') { + return this.config.query ? localize('searchTitle.withQuery', "Search: {0}", this.config.query) : localize('searchTitle', "Search"); + } + return localize('searchTitle.withQuery', "Search: {0}", basename(this.resource.path, '.code-search')); } setConfig(config: SearchConfiguration) { @@ -183,14 +216,39 @@ export class SearchEditorInput extends EditorInput { matches(other: unknown) { if (this === other) { return true; } - if (other instanceof UntitledTextEditorInput) { - if (other.getResource().fragment === this.resource.fragment) { return true; } - } + if (other instanceof SearchEditorInput) { - if (other.resource.path && other.resource.path === this.resource.path) { return true; } + if ( + (other.resource.path && other.resource.path === this.resource.path) || + (other.resource.fragment && other.resource.fragment === this.resource.fragment) + ) { + return true; + } } return false; } + + // Bringing this over from textFileService because it only suggests for untitled scheme. + // In the future I may just use the untitled scheme. I dont get particular benefit from using search-editor... + private suggestFileName(): URI { + const searchFileName = (this.config.query.replace(/[^\w \-_]+/g, '_') || 'Search') + '.code-search'; + + const remoteAuthority = this.environmentService.configuration.remoteAuthority; + const schemeFilter = remoteAuthority ? network.Schemas.vscodeRemote : network.Schemas.file; + + const lastActiveFile = this.historyService.getLastActiveFile(schemeFilter); + if (lastActiveFile) { + const lastDir = dirname(lastActiveFile); + return joinPath(lastDir, searchFileName); + } + + const lastActiveFolder = this.historyService.getLastActiveWorkspaceRoot(schemeFilter); + if (lastActiveFolder) { + return joinPath(lastActiveFolder, searchFileName); + } + + return URI.from({ scheme: schemeFilter, path: searchFileName }); + } } // Using \r\n on Windows inserts an extra newline between results. From b6fcbb1b1691219079f7065b2e68e2b0bfb22f76 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 14 Jan 2020 19:37:38 -0800 Subject: [PATCH 297/843] Add dirty indicators --- .../contrib/search/browser/searchEditor.ts | 6 ++++++ .../contrib/search/browser/searchEditorCommands.ts | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/vs/workbench/contrib/search/browser/searchEditor.ts b/src/vs/workbench/contrib/search/browser/searchEditor.ts index 055a5540426..de8d65295ec 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditor.ts @@ -162,6 +162,7 @@ export class SearchEditor extends BaseEditor { }); this._register(this.searchResultEditor.onDidChangeModel(() => this.hideHeader())); + this._register(this.searchResultEditor.onDidChangeModelContent(() => (this._input as SearchEditorInput)?.setDirty(true))); } private async runSearch(instant = false) { @@ -232,6 +233,7 @@ export class SearchEditor extends BaseEditor { const results = serializeSearchResultForEditor(searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, true); const textModel = assertIsDefined(this.searchResultEditor.getModel()); textModel.setValue(results.text.join(lineDelimiter)); + this.getInput()?.setDirty(this.getInput()?.resource.scheme !== 'search-editor'); this.hideHeader(); textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); @@ -269,6 +271,10 @@ export class SearchEditor extends BaseEditor { } } + private getInput(): SearchEditorInput | undefined { + return this._input as SearchEditorInput; + } + async setInput(newInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { await super.setInput(newInput, options, token); diff --git a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts index 93f3fc6ea7f..38be7b88471 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts @@ -131,6 +131,8 @@ export class SearchEditorInput extends EditorInput { private model: ITextModel; public readonly resource: URI; + private dirty: boolean = false; + constructor( config: SearchConfiguration | undefined, initialContents: string | undefined, @@ -169,11 +171,14 @@ export class SearchEditorInput extends EditorInput { const replacement = this.instantiationService.createInstance(SearchEditorInput, this.config, undefined, path); await this.editorService.replaceEditors([{ editor: this, replacement, options: { pinned: true } }], group); return true; + } else if (options?.context === SaveContext.EDITOR_CLOSE) { + return true; } } } return false; } else { + this.setDirty(false); return !!this.textFileService.write(this.resource, this.model.getValue(), options); } } @@ -209,6 +214,15 @@ export class SearchEditorInput extends EditorInput { return null; } + setDirty(dirty: boolean) { + this.dirty = dirty; + this._onDidChangeDirty.fire(); + } + + isDirty() { + return this.dirty; + } + dispose() { this.modelService.destroyModel(this.resource); super.dispose(); From b40e5ce729ce49b7311e92f65f1f21c514aa9c2c Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 14 Jan 2020 20:11:36 -0800 Subject: [PATCH 298/843] Add `matchienessHeuristic` to delay some regex searches. Fixes #88539. --- .../contrib/search/browser/searchWidget.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 30b5c34eb4a..c526a5f0fde 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -499,7 +499,31 @@ export class SearchWidget extends Widget { this.temporarilySkipSearchOnChange = false; } else { this._onSearchCancel.fire({ focus: false }); - this._searchDelayer.trigger((() => this.submitSearch(true)), this.searchConfiguration.searchOnTypeDebouncePeriod); + if (this.searchInput.getRegex()) { + try { + const regex = new RegExp(this.searchInput.getValue(), 'ug'); + const matchienessHeuristic = ` +~!@#$%^&*()_+ +\`1234567890-= +qwertyuiop[]\\ +QWERTYUIOP{}| +asdfghjkl;' +ASDFGHJKL:" +zxcvbnm,./ +ZXCVBNM<>? `.match(regex)?.length ?? 0; + + const delayMultiplier = + matchienessHeuristic < 50 ? 1 : + matchienessHeuristic < 100 ? 5 : // expressions like `.` or `\w` + 10; // only things matching empty string + + this._searchDelayer.trigger((() => this.submitSearch(true)), this.searchConfiguration.searchOnTypeDebouncePeriod * delayMultiplier); + } catch { + // pass + } + } else { + this._searchDelayer.trigger((() => this.submitSearch(true)), this.searchConfiguration.searchOnTypeDebouncePeriod); + } } } } From e2d7fc79a6d0b8d6f5c73e95dc90da1ceea003ee Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 07:02:42 +0100 Subject: [PATCH 299/843] #85216 Show download/upload options when syncing for first time --- .../userDataSync/browser/userDataSync.ts | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 5d87233cb66..e91b707a36c 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -35,6 +35,7 @@ import { IOutputService } from 'vs/workbench/contrib/output/common/output'; import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; import { IAuthenticationService, ChangeAccountEventData } from 'vs/workbench/services/authentication/browser/authenticationService'; import { Account } from 'vs/editor/common/modes'; +import { canceled, isPromiseCanceledError } from 'vs/base/common/errors'; const enum MSAAuthStatus { Initializing = 'Initializing', @@ -364,6 +365,8 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (this.authenticationState.get() === MSAAuthStatus.SignedOut) { await this.signIn(); } + + await this.handleFirstTimeSync(); await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, true); this.notificationService.info(localize('Sync Started', "Sync Started.")); } @@ -413,6 +416,36 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } + private async handleFirstTimeSync(): Promise { + + const hasRemote = await this.userDataSyncService.hasRemote(); + const hasPreviouslySynced = await this.userDataSyncService.hasPreviouslySynced(); + + if (hasRemote && !hasPreviouslySynced) { + const result = await this.dialogService.show( + Severity.Info, + localize('firs time sync', "First time synchronization"), + [ + localize('continue', "Continue"), + localize('cancel', "Cancel"), + localize('download', "Download"), + localize('upload', "Upload"), + ], + { + cancelId: 1, + detail: localize('first time sync detail', "Synchronising from this device for the first time. Would you like to \nDownload and replace with the remote data or \nUpload from this device and replace the remote data?") + } + ); + + switch (result.choice) { + case 1: return Promise.reject(canceled()); + case 2: return this.userDataSyncService.pull(); + // case 3: return this.userDataSyncService.push(); + case 3: return this.notificationService.info('not yet supported'); + } + } + } + private async turnOff(): Promise { const result = await this.dialogService.confirm({ type: 'info', @@ -509,7 +542,15 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo const turnOnSyncCommandId = 'workbench.userData.actions.syncStart'; const turnOnSyncWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.not(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.notEqualsTo(MSAAuthStatus.Initializing)); - CommandsRegistry.registerCommand(turnOnSyncCommandId, () => this.turnOn()); + CommandsRegistry.registerCommand(turnOnSyncCommandId, async () => { + try { + await this.turnOn(); + } catch (e) { + if (!isPromiseCanceledError(e)) { + this.notificationService.error(e); + } + } + }); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '5_sync', command: { From f85a3c815315679ecbe3ce595488c0c06fd0180a Mon Sep 17 00:00:00 2001 From: Jens Fischer Date: Tue, 14 Jan 2020 19:05:23 +0100 Subject: [PATCH 300/843] Clarify the docs for QuickPickItem.description and detail --- src/vs/vscode.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index eabfc93a66b..cf02afe365e 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1583,12 +1583,12 @@ declare module 'vscode' { label: string; /** - * A human-readable string which is rendered less prominent. + * A human-readable string which is rendered less prominent in the same line. */ description?: string; /** - * A human-readable string which is rendered less prominent. + * A human-readable string which is rendered less prominent in a separate line. */ detail?: string; From 78ba3c1ff4d5b162a810066914bfedaa98b1b07b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jan 2020 08:24:22 +0100 Subject: [PATCH 301/843] textfiles - no need to instantiate BaseTextEditorModel for "Save As" --- .../textfile/browser/textFileService.ts | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 5ee41d8fab9..6c32866863e 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -29,7 +29,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { coalesce } from 'vs/base/common/arrays'; import { trim } from 'vs/base/common/strings'; import { VSBuffer } from 'vs/base/common/buffer'; -import { ITextSnapshot } from 'vs/editor/common/model'; +import { ITextSnapshot, ITextModel } from 'vs/editor/common/model'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; @@ -535,12 +535,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex else { const textModel = this.modelService.getModel(source); if (textModel) { - const model = new BaseTextEditorModel(this.modelService, this.modeService, source); - if (model.isResolved()) { - success = await this.doSaveAsTextFile(model, source, target, options); - } - - model.dispose(); // free up + success = await this.doSaveAsTextFile(textModel, source, target, options); } } @@ -552,7 +547,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return target; } - private async doSaveAsTextFile(sourceModel: IResolvedTextEditorModel, source: URI, target: URI, options?: ITextFileSaveOptions): Promise { + private async doSaveAsTextFile(sourceModel: IResolvedTextEditorModel | ITextModel, source: URI, target: URI, options?: ITextFileSaveOptions): Promise { // Find source encoding if any let sourceModelEncoding: string | undefined = undefined; @@ -606,15 +601,29 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return false; } + let sourceTextModel: ITextModel | undefined = undefined; + if (sourceModel instanceof BaseTextEditorModel) { + if (sourceModel.isResolved()) { + sourceTextModel = sourceModel.textEditorModel; + } + } else { + sourceTextModel = sourceModel as ITextModel; + } + + let targetTextModel: ITextModel | undefined = undefined; + if (targetModel.isResolved()) { + targetTextModel = targetModel.textEditorModel; + } + // take over model value, encoding and mode (only if more specific) from source model targetModel.updatePreferredEncoding(sourceModelEncoding); - if (sourceModel.isResolved() && targetModel.isResolved()) { - this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot())); + if (sourceTextModel && targetTextModel) { + this.modelService.updateModel(targetTextModel, createTextBufferFactoryFromSnapshot(sourceTextModel.createSnapshot())); - const sourceMode = sourceModel.textEditorModel.getLanguageIdentifier(); - const targetMode = targetModel.textEditorModel.getLanguageIdentifier(); + const sourceMode = sourceTextModel.getLanguageIdentifier(); + const targetMode = targetTextModel.getLanguageIdentifier(); if (sourceMode.language !== PLAINTEXT_MODE_ID && targetMode.language === PLAINTEXT_MODE_ID) { - targetModel.textEditorModel.setMode(sourceMode); // only use if more specific than plain/text + targetTextModel.setMode(sourceMode); // only use if more specific than plain/text } } From 33c79d5ad447956814a2a3658029dffb9e28bae6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 09:27:19 +0100 Subject: [PATCH 302/843] Fix #88659 --- src/vs/workbench/browser/parts/views/views.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 5f19242c309..f817c344e4e 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -631,14 +631,17 @@ export class ViewsService extends Disposable implements IViewsService { this.viewDisposable.clear(); })); - this.viewContainersRegistry.all.forEach(viewContainer => { - const viewDescriptorCollection = this.viewDescriptorService.getViewDescriptors(viewContainer); - this.onViewsRegistered(viewDescriptorCollection.allViewDescriptors, viewContainer); - this._register(viewDescriptorCollection.onDidChangeViews(({ added, removed }) => { - this.onViewsRegistered(added, viewContainer); - this.onViewsDeregistered(removed, viewContainer); - })); - }); + this.viewContainersRegistry.all.forEach(viewContainer => this.onViewContainerRegistered(viewContainer)); + this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onViewContainerRegistered(viewContainer))); + } + + private onViewContainerRegistered(viewContainer: ViewContainer): void { + const viewDescriptorCollection = this.viewDescriptorService.getViewDescriptors(viewContainer); + this.onViewsRegistered(viewDescriptorCollection.allViewDescriptors, viewContainer); + this._register(viewDescriptorCollection.onDidChangeViews(({ added, removed }) => { + this.onViewsRegistered(added, viewContainer); + this.onViewsDeregistered(removed, viewContainer); + })); } private onViewsRegistered(views: IViewDescriptor[], container: ViewContainer): void { From de264a8bf54c6314eb44270b35d418b4b22e82a0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jan 2020 09:33:47 +0100 Subject: [PATCH 303/843] files - working copy service for explorer delete --- .../browser/parts/editor/editorAutoSave.ts | 4 +- .../backup/browser/backupOnShutdown.ts | 2 +- .../electron-browser/backupOnShutdown.ts | 6 +- .../contrib/files/browser/fileActions.ts | 141 +++++++++--------- .../contrib/files/browser/fileCommands.ts | 4 +- .../workingCopy/common/workingCopyService.ts | 86 ++++++----- .../test/common/workingCopyService.test.ts | 3 + 7 files changed, 125 insertions(+), 121 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts index 6be968e338f..4bf84682dba 100644 --- a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts +++ b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts @@ -133,8 +133,8 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution } private saveAllDirty(options?: ISaveOptions): void { - for (const workingCopy of this.workingCopyService.workingCopies) { - if (workingCopy.isDirty() && !(workingCopy.capabilities & WorkingCopyCapabilities.Untitled)) { + for (const workingCopy of this.workingCopyService.dirtyWorkingCopies) { + if (!(workingCopy.capabilities & WorkingCopyCapabilities.Untitled)) { workingCopy.save(options); } } diff --git a/src/vs/workbench/contrib/backup/browser/backupOnShutdown.ts b/src/vs/workbench/contrib/backup/browser/backupOnShutdown.ts index 8c17c752552..607607f678c 100644 --- a/src/vs/workbench/contrib/backup/browser/backupOnShutdown.ts +++ b/src/vs/workbench/contrib/backup/browser/backupOnShutdown.ts @@ -34,7 +34,7 @@ export class BackupOnShutdown extends Disposable implements IWorkbenchContributi // copies that have not been backed up yet and then prevent the // shutdown if that is the case. - const dirtyWorkingCopies = this.workingCopyService.workingCopies.filter(workingCopy => workingCopy.isDirty()); + const dirtyWorkingCopies = this.workingCopyService.dirtyWorkingCopies; if (!dirtyWorkingCopies.length) { return false; // no dirty: no veto } diff --git a/src/vs/workbench/contrib/backup/electron-browser/backupOnShutdown.ts b/src/vs/workbench/contrib/backup/electron-browser/backupOnShutdown.ts index 8e1053caa65..e6e17435bc3 100644 --- a/src/vs/workbench/contrib/backup/electron-browser/backupOnShutdown.ts +++ b/src/vs/workbench/contrib/backup/electron-browser/backupOnShutdown.ts @@ -46,7 +46,7 @@ export class BackupOnShutdown extends Disposable implements IWorkbenchContributi private onBeforeShutdown(reason: ShutdownReason): boolean | Promise { // Dirty working copies need treatment on shutdown - const dirtyWorkingCopies = this.workingCopyService.workingCopies.filter(workingCopy => workingCopy.isDirty()); + const dirtyWorkingCopies = this.workingCopyService.dirtyWorkingCopies; if (dirtyWorkingCopies.length) { // If auto save is enabled, save all working copies and then check again for dirty copies @@ -55,7 +55,7 @@ export class BackupOnShutdown extends Disposable implements IWorkbenchContributi return this.doSaveAll(dirtyWorkingCopies, false /* not untitled */, { skipSaveParticipants: true }).then(() => { // If we still have dirty working copies, we either have untitled ones or working copies that cannot be saved - const remainingDirtyWorkingCopies = this.workingCopyService.workingCopies.filter(workingCopy => workingCopy.isDirty()); + const remainingDirtyWorkingCopies = this.workingCopyService.dirtyWorkingCopies; if (remainingDirtyWorkingCopies.length) { return this.handleDirtyBeforeShutdown(remainingDirtyWorkingCopies, reason); } @@ -143,7 +143,7 @@ export class BackupOnShutdown extends Disposable implements IWorkbenchContributi private async confirmBeforeShutdown(): Promise { // Show confirm dialog for all dirty working copies - const dirtyWorkingCopies = this.workingCopyService.workingCopies.filter(workingCopy => workingCopy.isDirty()); + const dirtyWorkingCopies = this.workingCopyService.dirtyWorkingCopies; const confirm = await this.fileDialogService.showSaveConfirm(dirtyWorkingCopies.map(w => w.resource)); // Save diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 3774785acbc..c6ccc289d7d 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -143,7 +143,7 @@ export class GlobalNewUntitledFileAction extends Action { } } -async function deleteFiles(textFileService: ITextFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise { +async function deleteFiles(workingCopyService: IWorkingCopyService, textFileService: ITextFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise { let primaryButton: string; if (useTrash) { primaryButton = isWindows ? nls.localize('deleteButtonLabelRecycleBin', "&&Move to Recycle Bin") : nls.localize({ key: 'deleteButtonLabelTrash', comment: ['&& denotes a mnemonic'] }, "&&Move to Trash"); @@ -155,16 +155,16 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi // Handle dirty let confirmed = true; - const dirty = textFileService.getDirty().filter(d => distinctElements.some(e => resources.isEqualOrParent(d, e.resource))); - if (dirty.length) { + const dirtyWorkingCopies = workingCopyService.dirtyWorkingCopies.filter(workingCopy => distinctElements.some(e => resources.isEqualOrParent(workingCopy.resource, e.resource))); + if (dirtyWorkingCopies.length) { let message: string; if (distinctElements.length > 1) { message = nls.localize('dirtyMessageFilesDelete', "You are deleting files with unsaved changes. Do you want to continue?"); } else if (distinctElements[0].isDirectory) { - if (dirty.length === 1) { + if (dirtyWorkingCopies.length === 1) { message = nls.localize('dirtyMessageFolderOneDelete', "You are deleting a folder with unsaved changes in 1 file. Do you want to continue?"); } else { - message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder with unsaved changes in {0} files. Do you want to continue?", dirty.length); + message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder with unsaved changes in {0} files. Do you want to continue?", dirtyWorkingCopies.length); } } else { message = nls.localize('dirtyMessageFileDelete', "You are deleting a file with unsaved changes. Do you want to continue?"); @@ -181,7 +181,7 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi confirmed = false; } else { skipConfirm = true; - await textFileService.revertAll(dirty); + await Promise.all(dirtyWorkingCopies.map(dirty => dirty.revert())); } } @@ -190,11 +190,11 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi return; } - let confirmDeletePromise: Promise; + let confirmation: IConfirmationResult; // Check if we need to ask for confirmation at all if (skipConfirm || (useTrash && configurationService.getValue(CONFIRM_DELETE_SETTING_KEY) === false)) { - confirmDeletePromise = Promise.resolve({ confirmed: true }); + confirmation = { confirmed: true }; } // Confirm for moving to trash @@ -207,7 +207,7 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi detail += distinctElements.length > 1 ? nls.localize('undoTrashFiles', "You can restore these files from the Trash.") : nls.localize('undoTrash', "You can restore this file from the Trash."); } - confirmDeletePromise = dialogService.confirm({ + confirmation = await dialogService.confirm({ message, detail, primaryButton, @@ -223,7 +223,7 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi let { message, detail } = getDeleteMessage(distinctElements); detail += detail ? '\n' : ''; detail += nls.localize('irreversible', "This action is irreversible!"); - confirmDeletePromise = dialogService.confirm({ + confirmation = await dialogService.confirm({ message, detail, primaryButton, @@ -231,61 +231,54 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi }); } - return confirmDeletePromise.then(confirmation => { - // Check for confirmation checkbox - let updateConfirmSettingsPromise: Promise = Promise.resolve(undefined); - if (confirmation.confirmed && confirmation.checkboxChecked === true) { - updateConfirmSettingsPromise = configurationService.updateValue(CONFIRM_DELETE_SETTING_KEY, false, ConfigurationTarget.USER); + + // Check for confirmation checkbox + if (confirmation.confirmed && confirmation.checkboxChecked === true) { + await configurationService.updateValue(CONFIRM_DELETE_SETTING_KEY, false, ConfigurationTarget.USER); + } + + + // Check for confirmation + if (!confirmation.confirmed) { + return; + } + + // Call function + try { + await Promise.all(distinctElements.map(e => textFileService.delete(e.resource, { useTrash: useTrash, recursive: true }))); + } catch (error) { + + // Handle error to delete file(s) from a modal confirmation dialog + let errorMessage: string; + let detailMessage: string | undefined; + let primaryButton: string; + if (useTrash) { + errorMessage = isWindows ? nls.localize('binFailed', "Failed to delete using the Recycle Bin. Do you want to permanently delete instead?") : nls.localize('trashFailed', "Failed to delete using the Trash. Do you want to permanently delete instead?"); + detailMessage = nls.localize('irreversible', "This action is irreversible!"); + primaryButton = nls.localize({ key: 'deletePermanentlyButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete Permanently"); + } else { + errorMessage = toErrorMessage(error, false); + primaryButton = nls.localize({ key: 'retryButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Retry"); } - return updateConfirmSettingsPromise.then(() => { + const res = await dialogService.confirm({ + message: errorMessage, + detail: detailMessage, + type: 'warning', + primaryButton + }); - // Check for confirmation - if (!confirmation.confirmed) { - return Promise.resolve(undefined); + if (res.confirmed) { + if (useTrash) { + useTrash = false; // Delete Permanently } - // Call function - const servicePromise = Promise.all(distinctElements.map(e => textFileService.delete(e.resource, { useTrash: useTrash, recursive: true }))) - .then(undefined, (error: any) => { - // Handle error to delete file(s) from a modal confirmation dialog - let errorMessage: string; - let detailMessage: string | undefined; - let primaryButton: string; - if (useTrash) { - errorMessage = isWindows ? nls.localize('binFailed', "Failed to delete using the Recycle Bin. Do you want to permanently delete instead?") : nls.localize('trashFailed', "Failed to delete using the Trash. Do you want to permanently delete instead?"); - detailMessage = nls.localize('irreversible', "This action is irreversible!"); - primaryButton = nls.localize({ key: 'deletePermanentlyButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete Permanently"); - } else { - errorMessage = toErrorMessage(error, false); - primaryButton = nls.localize({ key: 'retryButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Retry"); - } + skipConfirm = true; - return dialogService.confirm({ - message: errorMessage, - detail: detailMessage, - type: 'warning', - primaryButton - }).then(res => { - - if (res.confirmed) { - if (useTrash) { - useTrash = false; // Delete Permanently - } - - skipConfirm = true; - - return deleteFiles(textFileService, dialogService, configurationService, elements, useTrash, skipConfirm); - } - - return Promise.resolve(); - }); - }); - - return servicePromise.then(undefined); - }); - }); + return deleteFiles(workingCopyService, textFileService, dialogService, configurationService, elements, useTrash, skipConfirm); + } + } } function getMoveToTrashMessage(distinctElements: ExplorerItem[]): { message: string, detail: string } { @@ -648,7 +641,7 @@ export class ShowActiveFileInExplorer extends Action { super(id, label); } - run(): Promise { + async run(): Promise { const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); if (resource) { this.commandService.executeCommand(REVEAL_IN_EXPLORER_COMMAND_ID, resource); @@ -656,7 +649,7 @@ export class ShowActiveFileInExplorer extends Action { this.notificationService.info(nls.localize('openFileToShow', "Open a file first to show it in the explorer")); } - return Promise.resolve(true); + return true; } } @@ -726,7 +719,7 @@ export class ShowOpenedFileInNewWindow extends Action { super(id, label); } - run(): Promise { + async run(): Promise { const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); if (fileResource) { if (this.fileService.canHandleResource(fileResource)) { @@ -738,7 +731,7 @@ export class ShowOpenedFileInNewWindow extends Action { this.notificationService.info(nls.localize('openFileToShowInNewWindow.nofile', "Open a file first to open in new window")); } - return Promise.resolve(true); + return true; } } @@ -822,7 +815,7 @@ export class CompareWithClipboardAction extends Action { this.enabled = true; } - run(): Promise { + async run(): Promise { const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); if (resource && (this.fileService.canHandleResource(resource) || resource.scheme === Schemas.untitled)) { if (!this.registrationDisposal) { @@ -839,7 +832,7 @@ export class CompareWithClipboardAction extends Action { }); } - return Promise.resolve(true); + return true; } dispose(): void { @@ -902,15 +895,17 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole folder.addChild(newStat); - const onSuccess = (value: string): Promise => { - const createPromise = isFolder ? fileService.createFolder(resources.joinPath(folder.resource, value)) : textFileService.create(resources.joinPath(folder.resource, value)); - return createPromise.then(created => { + const onSuccess = async (value: string): Promise => { + try { + const created = isFolder ? await fileService.createFolder(resources.joinPath(folder.resource, value)) : await textFileService.create(resources.joinPath(folder.resource, value)); refreshIfSeparator(value, explorerService); - return isFolder ? explorerService.select(created.resource, true) - : editorService.openEditor({ resource: created.resource, options: { pinned: true } }).then(() => undefined); - }, error => { + + isFolder ? + await explorerService.select(created.resource, true) : + await editorService.openEditor({ resource: created.resource, options: { pinned: true } }); + } catch (error) { onErrorWithRetry(notificationService, error, () => onSuccess(value)); - }); + } }; explorerService.setEditable(newStat, { @@ -974,7 +969,7 @@ export const moveFileToTrashHandler = async (accessor: ServicesAccessor) => { const explorerService = accessor.get(IExplorerService); const stats = explorerService.getContext(true).filter(s => !s.isRoot); if (stats.length) { - await deleteFiles(accessor.get(ITextFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, true); + await deleteFiles(accessor.get(IWorkingCopyService), accessor.get(ITextFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, true); } }; @@ -983,7 +978,7 @@ export const deleteFileHandler = async (accessor: ServicesAccessor) => { const stats = explorerService.getContext(true).filter(s => !s.isRoot); if (stats.length) { - await deleteFiles(accessor.get(ITextFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, false); + await deleteFiles(accessor.get(IWorkingCopyService), accessor.get(ITextFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, false); } }; diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index bb781656124..cf6c906c91d 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -200,7 +200,7 @@ CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand({ id: COMPARE_SELECTED_COMMAND_ID, - handler: (accessor, resource: URI | object) => { + handler: async (accessor, resource: URI | object) => { const editorService = accessor.get(IEditorService); const explorerService = accessor.get(IExplorerService); const resources = getMultiSelectedResources(resource, accessor.get(IListService), editorService, explorerService); @@ -212,7 +212,7 @@ CommandsRegistry.registerCommand({ }); } - return Promise.resolve(true); + return true; } }); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index c3543e4c824..3193a3dd81c 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -81,6 +81,8 @@ export interface IWorkingCopyService { readonly dirtyCount: number; + readonly dirtyWorkingCopies: IWorkingCopy[]; + readonly hasDirty: boolean; isDirty(resource: URI): boolean; @@ -118,46 +120,6 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic //#endregion - //#region Dirty Tracking - - isDirty(resource: URI): boolean { - const workingCopies = this.mapResourceToWorkingCopy.get(resource.toString()); - if (workingCopies) { - for (const workingCopy of workingCopies) { - if (workingCopy.isDirty()) { - return true; - } - } - } - - return false; - } - - get hasDirty(): boolean { - for (const workingCopy of this._workingCopies) { - if (workingCopy.isDirty()) { - return true; - } - } - - return false; - } - - get dirtyCount(): number { - let totalDirtyCount = 0; - - for (const workingCopy of this._workingCopies) { - if (workingCopy.isDirty()) { - totalDirtyCount++; - } - } - - return totalDirtyCount; - } - - //#endregion - - //#region Registry private mapResourceToWorkingCopy = TernarySearchTree.forPaths>(); @@ -216,6 +178,50 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic } //#endregion + + + //#region Dirty Tracking + + get hasDirty(): boolean { + for (const workingCopy of this._workingCopies) { + if (workingCopy.isDirty()) { + return true; + } + } + + return false; + } + + get dirtyCount(): number { + let totalDirtyCount = 0; + + for (const workingCopy of this._workingCopies) { + if (workingCopy.isDirty()) { + totalDirtyCount++; + } + } + + return totalDirtyCount; + } + + get dirtyWorkingCopies(): IWorkingCopy[] { + return this.workingCopies.filter(workingCopy => workingCopy.isDirty()); + } + + isDirty(resource: URI): boolean { + const workingCopies = this.mapResourceToWorkingCopy.get(resource.toString()); + if (workingCopies) { + for (const workingCopy of workingCopies) { + if (workingCopy.isDirty()) { + return true; + } + } + } + + return false; + } + + //#endregion } registerSingleton(IWorkingCopyService, WorkingCopyService, true); diff --git a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts index b94eb61e384..a6a5269f462 100644 --- a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts @@ -105,7 +105,10 @@ suite('WorkingCopyService', () => { copy1.setDirty(true); + assert.equal(copy1.isDirty(), true); assert.equal(service.dirtyCount, 1); + assert.equal(service.dirtyWorkingCopies.length, 1); + assert.equal(service.dirtyWorkingCopies[0], copy1); assert.equal(service.isDirty(resource1), true); assert.equal(service.hasDirty, true); assert.equal(onDidChangeDirty.length, 1); From 3d5b61c8435f651a3788062978ea344abdf865e5 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 15 Jan 2020 09:56:26 +0100 Subject: [PATCH 304/843] fixes #86822 --- src/vs/workbench/contrib/files/browser/fileActions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index c6ccc289d7d..fa1c63a1df7 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -162,12 +162,12 @@ async function deleteFiles(workingCopyService: IWorkingCopyService, textFileServ message = nls.localize('dirtyMessageFilesDelete', "You are deleting files with unsaved changes. Do you want to continue?"); } else if (distinctElements[0].isDirectory) { if (dirtyWorkingCopies.length === 1) { - message = nls.localize('dirtyMessageFolderOneDelete', "You are deleting a folder with unsaved changes in 1 file. Do you want to continue?"); + message = nls.localize('dirtyMessageFolderOneDelete', "You are deleting a folder {0} with unsaved changes in 1 file. Do you want to continue?", distinctElements[0].name); } else { - message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder with unsaved changes in {0} files. Do you want to continue?", dirtyWorkingCopies.length); + message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder {0} with unsaved changes in {1} files. Do you want to continue?", distinctElements[0].name, dirtyWorkingCopies.length); } } else { - message = nls.localize('dirtyMessageFileDelete', "You are deleting a file with unsaved changes. Do you want to continue?"); + message = nls.localize('dirtyMessageFileDelete', "You are deleting {0} with unsaved changes. Do you want to continue?", distinctElements[0].name); } const response = await dialogService.confirm({ From 31ec4fe4346dddf99f53f3aecfd529081febdbae Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 15 Jan 2020 10:37:33 +0100 Subject: [PATCH 305/843] CompletionItemLabel#label -> name --- src/vs/editor/common/modes.ts | 6 ++---- src/vs/editor/contrib/suggest/completionModel.ts | 2 +- src/vs/editor/contrib/suggest/suggest.ts | 4 ++-- src/vs/editor/contrib/suggest/suggestController.ts | 2 +- src/vs/editor/contrib/suggest/suggestWidget.ts | 6 +++--- src/vs/editor/contrib/suggest/wordDistance.ts | 2 +- src/vs/monaco.d.ts | 6 ++---- src/vs/vscode.proposed.d.ts | 6 ++---- src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts | 2 +- src/vs/workbench/api/common/extHostLanguageFeatures.ts | 2 +- src/vs/workbench/api/common/extHostTypes.ts | 2 +- 11 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index f22125a6acb..3722d37916a 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -372,11 +372,9 @@ export let completionKindFromString: { export interface CompletionItemLabel { /** - * The label of this completion item. By default - * this is also the text that is inserted when selecting - * this completion. + * The name of this completion item's label. */ - label: string; + name: string; /** * A description of the completion item which is rendered diff --git a/src/vs/editor/contrib/suggest/completionModel.ts b/src/vs/editor/contrib/suggest/completionModel.ts index 5382be986e9..5013d8c1734 100644 --- a/src/vs/editor/contrib/suggest/completionModel.ts +++ b/src/vs/editor/contrib/suggest/completionModel.ts @@ -192,7 +192,7 @@ export class CompletionModel { } } - const label = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.label; + const label = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name; if (wordPos >= wordLen) { // the wordPos at which scoring starts is the whole word // and therefore the same rules as not having a word apply diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index 61813788b10..c1f13090b28 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -59,7 +59,7 @@ export class CompletionItem { ) { this.textLabel = typeof completion.label === 'string' ? completion.label - : completion.label.label; + : completion.label.name; // ensure lower-variants (perf) this.labelLow = this.textLabel.toLowerCase(); @@ -191,7 +191,7 @@ export function provideSuggestionItems( } // fill in default sortText when missing if (!suggestion.sortText) { - suggestion.sortText = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.label; + suggestion.sortText = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name; } allSuggestions.push(new CompletionItem(position, suggestion, container, provider, model)); diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index b0fa140844a..8964cf24ecb 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -343,7 +343,7 @@ export class SuggestController implements IEditorContribution { } private _alertCompletionItem({ completion: suggestion }: CompletionItem): void { - const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.label; + const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name; if (isNonEmptyArray(suggestion.additionalTextEdits)) { let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' made {1} additional edits", textLabel, suggestion.additionalTextEdits.length); alert(msg); diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 90f1a7ba65d..aca32568984 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -69,7 +69,7 @@ const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1 function extractColor(item: CompletionItem, out: string[]): boolean { const label = typeof item.completion.label === 'string' ? item.completion.label - : item.completion.label.label; + : item.completion.label.name; if (label.match(colorRegExp)) { out[0] = label; @@ -167,7 +167,7 @@ class Renderer implements IListRenderer renderElement(element: CompletionItem, _index: number, templateData: ISuggestionTemplateData): void { const data = templateData; const suggestion = (element).completion; - const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.label; + const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name; data.icon.className = 'icon ' + completionKindToCssClass(suggestion.kind); data.colorspan.style.backgroundColor = ''; @@ -630,7 +630,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate must have at least a label'); diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 19863726fac..97d0d97feb2 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1352,7 +1352,7 @@ export enum CompletionItemTag { } export interface CompletionItemLabel { - label: string; + name: string; // description?: string; details?: string; } From 9b6ae0ca7bf783ebf47255040836e87b66591ba0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jan 2020 10:39:45 +0100 Subject: [PATCH 306/843] textfiles - reduce service methods that are not needed anymore --- .../electron-browser/backupOnShutdown.test.ts | 12 +- .../files/browser/views/explorerViewer.ts | 8 +- .../contrib/search/browser/replaceService.ts | 109 +++++++------ .../common/configurationEditingService.ts | 2 +- .../textfile/browser/textFileService.ts | 147 +++++------------- .../services/textfile/common/textfiles.ts | 27 +--- .../textfile/test/textFileEditorModel.test.ts | 4 +- .../textfile/test/textFileService.test.ts | 86 ++++------ .../workingCopy/common/workingCopyService.ts | 8 + .../test/common/workingCopyService.test.ts | 6 + .../workbench/test/workbenchTestServices.ts | 4 + 11 files changed, 162 insertions(+), 251 deletions(-) diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupOnShutdown.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupOnShutdown.test.ts index db140201650..e8d36fc4af6 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupOnShutdown.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupOnShutdown.test.ts @@ -22,6 +22,7 @@ import { IFilesConfigurationService } from 'vs/workbench/services/filesConfigura import { IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs'; import { BackupOnShutdown } from 'vs/workbench/contrib/backup/electron-browser/backupOnShutdown'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; class ServiceAccessor { constructor( @@ -34,7 +35,8 @@ class ServiceAccessor { @IFileService public fileService: TestFileService, @IElectronService public electronService: TestElectronService, @IFileDialogService public fileDialogService: TestFileDialogService, - @IBackupFileService public backupFileService: TestBackupFileService + @IBackupFileService public backupFileService: TestBackupFileService, + @IWorkingCopyService public workingCopyService: IWorkingCopyService ) { } } @@ -94,7 +96,7 @@ suite('BackupOnShutdown', () => { await model.load(); model.textEditorModel!.setValue('foo'); - assert.equal(accessor.textFileService.getDirty().length, 1); + assert.equal(accessor.workingCopyService.dirtyCount, 1); const event = new BeforeShutdownEventImpl(); accessor.lifecycleService.fireWillShutdown(event); @@ -110,7 +112,7 @@ suite('BackupOnShutdown', () => { await model.load(); model.textEditorModel!.setValue('foo'); - assert.equal(accessor.textFileService.getDirty().length, 1); + assert.equal(accessor.workingCopyService.dirtyCount, 1); const event = new BeforeShutdownEventImpl(); accessor.lifecycleService.fireWillShutdown(event); @@ -135,7 +137,7 @@ suite('BackupOnShutdown', () => { await model.load(); model.textEditorModel!.setValue('foo'); - assert.equal(accessor.textFileService.getDirty().length, 1); + assert.equal(accessor.workingCopyService.dirtyCount, 1); const event = new BeforeShutdownEventImpl(); accessor.lifecycleService.fireWillShutdown(event); @@ -269,7 +271,7 @@ suite('BackupOnShutdown', () => { await model.load(); model.textEditorModel!.setValue('foo'); - assert.equal(accessor.textFileService.getDirty().length, 1); + assert.equal(accessor.workingCopyService.dirtyCount, 1); const event = new BeforeShutdownEventImpl(); event.reason = shutdownReason; diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index c98ad1df6a6..3553eb9385c 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -55,6 +55,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { isNumber } from 'vs/base/common/types'; import { domEvent } from 'vs/base/browser/event'; import { IEditableData } from 'vs/workbench/common/views'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -642,7 +643,8 @@ export class FileDragAndDrop implements ITreeDragAndDrop { @IInstantiationService private instantiationService: IInstantiationService, @ITextFileService private textFileService: ITextFileService, @IHostService private hostService: IHostService, - @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService + @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, + @IWorkingCopyService private workingCopyService: IWorkingCopyService ) { this.toDispose = []; @@ -946,8 +948,8 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // if the target exists and is dirty, make sure to revert it. otherwise the dirty contents // of the target file would replace the contents of the added file. since we already // confirmed the overwrite before, this is OK. - if (this.textFileService.isDirty(targetFile)) { - await this.textFileService.revertAll([targetFile], { soft: true }); + if (this.workingCopyService.isDirty(targetFile)) { + await Promise.all(this.workingCopyService.getWorkingCopies(targetFile).map(workingCopy => workingCopy.revert({ soft: true }))); } const copyTarget = joinPath(target.resource, basename(sourceFile)); diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index 4f962476861..8cab5f81de8 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as errors from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import * as network from 'vs/base/common/network'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -65,21 +64,19 @@ class ReplacePreviewModel extends Disposable { super(); } - resolve(replacePreviewUri: URI): Promise { + async resolve(replacePreviewUri: URI): Promise { const fileResource = toFileResource(replacePreviewUri); const fileMatch = this.searchWorkbenchService.searchModel.searchResult.matches().filter(match => match.resource.toString() === fileResource.toString())[0]; - return this.textModelResolverService.createModelReference(fileResource).then(ref => { - ref = this._register(ref); - const sourceModel = ref.object.textEditorModel; - const sourceModelModeId = sourceModel.getLanguageIdentifier().language; - const replacePreviewModel = this.modelService.createModel(createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), this.modeService.create(sourceModelModeId), replacePreviewUri); - this._register(fileMatch.onChange(modelChange => this.update(sourceModel, replacePreviewModel, fileMatch, modelChange))); - this._register(this.searchWorkbenchService.searchModel.onReplaceTermChanged(() => this.update(sourceModel, replacePreviewModel, fileMatch))); - this._register(fileMatch.onDispose(() => replacePreviewModel.dispose())); // TODO@Sandeep we should not dispose a model directly but rather the reference (depends on https://github.com/Microsoft/vscode/issues/17073) - this._register(replacePreviewModel.onWillDispose(() => this.dispose())); - this._register(sourceModel.onWillDispose(() => this.dispose())); - return replacePreviewModel; - }); + const ref = this._register(await this.textModelResolverService.createModelReference(fileResource)); + const sourceModel = ref.object.textEditorModel; + const sourceModelModeId = sourceModel.getLanguageIdentifier().language; + const replacePreviewModel = this.modelService.createModel(createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), this.modeService.create(sourceModelModeId), replacePreviewUri); + this._register(fileMatch.onChange(modelChange => this.update(sourceModel, replacePreviewModel, fileMatch, modelChange))); + this._register(this.searchWorkbenchService.searchModel.onReplaceTermChanged(() => this.update(sourceModel, replacePreviewModel, fileMatch))); + this._register(fileMatch.onDispose(() => replacePreviewModel.dispose())); // TODO@Sandeep we should not dispose a model directly but rather the reference (depends on https://github.com/Microsoft/vscode/issues/17073) + this._register(replacePreviewModel.onWillDispose(() => this.dispose())); + this._register(sourceModel.onWillDispose(() => this.dispose())); + return replacePreviewModel; } private update(sourceModel: ITextModel, replacePreviewModel: ITextModel, fileMatch: FileMatch, override: boolean = false): void { @@ -103,15 +100,24 @@ export class ReplaceService implements IReplaceService { replace(match: Match): Promise; replace(files: FileMatch[], progress?: IProgress): Promise; replace(match: FileMatchOrMatch, progress?: IProgress, resource?: URI): Promise; - replace(arg: any, progress: IProgress | undefined = undefined, resource: URI | null = null): Promise { + async replace(arg: any, progress: IProgress | undefined = undefined, resource: URI | null = null): Promise { const edits: ResourceTextEdit[] = this.createEdits(arg, resource); - return this.bulkEditorService.apply({ edits }, { progress }).then(() => this.textFileService.saveAll(edits.map(e => e.resource))); + await this.bulkEditorService.apply({ edits }, { progress }); + + return Promise.all(edits.map(e => { + const model = this.textFileService.models.get(e.resource); + if (model) { + return model.save(); + } + + return Promise.resolve(undefined); + })); } - openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { + async openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { const fileMatch = element instanceof Match ? element.parent() : element; - return this.editorService.openEditor({ + const editor = await this.editorService.openEditor({ leftResource: fileMatch.resource, rightResource: toReplaceResource(fileMatch.resource), label: nls.localize('fileReplaceChanges', "{0} ↔ {1} (Replace Preview)", fileMatch.name(), fileMatch.name()), @@ -120,46 +126,39 @@ export class ReplaceService implements IReplaceService { pinned, revealIfVisible: true } - }).then(editor => { - const input = editor?.input; - const disposable = fileMatch.onDispose(() => { - if (input) { - input.dispose(); - } - disposable.dispose(); - }); - this.updateReplacePreview(fileMatch).then(() => { - if (editor) { - const editorControl = editor.getControl(); - if (element instanceof Match && editorControl) { - editorControl.revealLineInCenter(element.range().startLineNumber, ScrollType.Immediate); - } - } - }); - }, errors.onUnexpectedError); + }); + const input = editor?.input; + const disposable = fileMatch.onDispose(() => { + if (input) { + input.dispose(); + } + disposable.dispose(); + }); + await this.updateReplacePreview(fileMatch); + if (editor) { + const editorControl = editor.getControl(); + if (element instanceof Match && editorControl) { + editorControl.revealLineInCenter(element.range().startLineNumber, ScrollType.Immediate); + } + } } - updateReplacePreview(fileMatch: FileMatch, override: boolean = false): Promise { + async updateReplacePreview(fileMatch: FileMatch, override: boolean = false): Promise { const replacePreviewUri = toReplaceResource(fileMatch.resource); - return Promise.all([this.textModelResolverService.createModelReference(fileMatch.resource), this.textModelResolverService.createModelReference(replacePreviewUri)]) - .then(([sourceModelRef, replaceModelRef]) => { - const sourceModel = sourceModelRef.object.textEditorModel; - const replaceModel = replaceModelRef.object.textEditorModel; - const returnValue = Promise.resolve(null); - // If model is disposed do not update - if (sourceModel && replaceModel) { - if (override) { - replaceModel.setValue(sourceModel.getValue()); - } else { - replaceModel.undo(); - } - this.applyEditsToPreview(fileMatch, replaceModel); - } - return returnValue.then(() => { - sourceModelRef.dispose(); - replaceModelRef.dispose(); - }); - }); + const [sourceModelRef, replaceModelRef] = await Promise.all([this.textModelResolverService.createModelReference(fileMatch.resource), this.textModelResolverService.createModelReference(replacePreviewUri)]); + const sourceModel = sourceModelRef.object.textEditorModel; + const replaceModel = replaceModelRef.object.textEditorModel; + // If model is disposed do not update + if (sourceModel && replaceModel) { + if (override) { + replaceModel.setValue(sourceModel.getValue()); + } else { + replaceModel.undo(); + } + this.applyEditsToPreview(fileMatch, replaceModel); + } + sourceModelRef.dispose(); + replaceModelRef.dispose(); } private applyEditsToPreview(fileMatch: FileMatch, replaceModel: ITextModel): void { diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index e313353286b..b9b233360a7 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -487,7 +487,7 @@ export class ConfigurationEditingService { } // Target cannot be dirty if not writing into buffer - if (checkDirty && this.textFileService.isDirty(operation.resource)) { + if (checkDirty && operation.resource && this.textFileService.isDirty(operation.resource)) { return this.reject(ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY, target, operation); } return reference; diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 6c32866863e..d5bbe8bc182 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -177,7 +177,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex await this._onWillRunOperation.fireAsync({ operation: FileOperation.DELETE, target: resource }, CancellationToken.None); const dirtyFiles = this.getDirty().filter(dirty => isEqualOrParent(dirty, resource)); - await this.revertAll(dirtyFiles, { soft: true }); + await this.doRevertAll(dirtyFiles, { soft: true }); await this.fileService.del(resource, options); @@ -243,7 +243,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // in order to move and copy, we need to soft revert all dirty models, // both from the source as well as the target if any const dirtyModels = [...sourceModels, ...conflictingModels].filter(model => model.isDirty()); - await this.revertAll(dirtyModels.map(dirtyModel => dirtyModel.resource), { soft: true }); + await this.doRevertAll(dirtyModels.map(dirtyModel => dirtyModel.resource), { soft: true }); // now we can rename the source to target via file operation let stat: IFileStatWithMetadata; @@ -290,85 +290,44 @@ export abstract class AbstractTextFileService extends Disposable implements ITex //#region save async save(resource: URI, options?: ITextFileSaveOptions): Promise { - return !(await this.saveAll([resource], options)).results.some(result => result.error); - } - saveAll(includeUntitled?: boolean, options?: ITextFileSaveOptions): Promise; - saveAll(resources: URI[], options?: ITextFileSaveOptions): Promise; - saveAll(arg1?: boolean | URI[], options?: ITextFileSaveOptions): Promise { - - // Extract the resources to save - let resourcesToSave: URI[] = []; - if (Array.isArray(arg1)) { - // if specific resources are given, we consider even - // non-dirty ones if options.force is provided - if (options?.force) { - resourcesToSave = arg1; - } else { - resourcesToSave = this.getDirty(arg1); - } - } else { - // if no resources are given, we only consider dirty - // resources even if options.force is provided - resourcesToSave = this.getDirty(); - } - - // split up between files and untitled - const filesToSave: URI[] = []; - const untitledToSave: URI[] = []; - resourcesToSave.forEach(resourceToSave => { - if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && resourceToSave.scheme === Schemas.untitled) { - untitledToSave.push(resourceToSave); - } else if (this.fileService.canHandleResource(resourceToSave)) { - filesToSave.push(resourceToSave); - } - }); - - return this.doSaveAll(filesToSave, untitledToSave, options); - } - - private async doSaveAll(fileResources: URI[], untitledResources: URI[], options?: ITextFileSaveOptions): Promise { - - // Handle files first that can just be saved - const result = await this.doSaveAllFiles(fileResources, options); - - // Preflight for untitled to handle cancellation from the dialog - const targetsForUntitled: URI[] = []; - for (const untitled of untitledResources) { - if (this.untitledTextEditorService.exists(untitled)) { - let targetUri: URI; + // Untitled + if (resource.scheme === Schemas.untitled) { + if (this.untitledTextEditorService.exists(resource)) { + let targetUri: URI | undefined; // Untitled with associated file path don't need to prompt - if (this.untitledTextEditorService.hasAssociatedFilePath(untitled)) { - targetUri = toLocalResource(untitled, this.environmentService.configuration.remoteAuthority); + if (this.untitledTextEditorService.hasAssociatedFilePath(resource)) { + targetUri = toLocalResource(resource, this.environmentService.configuration.remoteAuthority); } // Otherwise ask user else { - const targetPath = await this.promptForPath(untitled, this.suggestFileName(untitled)); - if (!targetPath) { - return { results: [...fileResources, ...untitledResources].map(r => ({ source: r })) }; - } - - targetUri = targetPath; + targetUri = await this.promptForPath(resource, this.suggestFileName(resource)); } - targetsForUntitled.push(targetUri); + // Save as if target provided + if (targetUri) { + await this.saveAs(resource, targetUri, options); + + return true; + } } } - // Handle untitled - await Promise.all(targetsForUntitled.map(async (target, index) => { - const uri = await this.saveAs(untitledResources[index], target); + // File + else { + const model = this.models.get(resource); + if (model) { - result.results.push({ - source: untitledResources[index], - target: uri, - error: !uri // the operation was canceled or failed, so mark as error - }); - })); + // Save with options + await model.save(options); - return result; + return !model.isDirty(); + } + } + + return false; } protected async promptForPath(resource: URI, defaultUri: URI, availableFileSystems?: readonly string[]): Promise { @@ -432,32 +391,6 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return options; } - private async doSaveAllFiles(resources?: URI[], options: ITextFileSaveOptions = Object.create(null)): Promise { - const fileModelsToSave = this.getFileModels(resources); - - const mapResourceToResult = new ResourceMap(); - for (const fileModelToSave of fileModelsToSave) { - mapResourceToResult.set(fileModelToSave.resource, { source: fileModelToSave.resource }); - } - - // Save all in parallel - await Promise.all(fileModelsToSave.map(async model => { - - // Save with options - await model.save(options); - - // If model is still dirty, mark the resulting operation as error - if (model.isDirty()) { - const result = mapResourceToResult.get(model.resource); - if (result) { - result.error = true; - } - } - })); - - return { results: mapResourceToResult.values() }; - } - private getFileModels(arg1?: URI | URI[]): ITextFileEditorModel[] { if (Array.isArray(arg1)) { const models: ITextFileEditorModel[] = []; @@ -682,10 +615,10 @@ export abstract class AbstractTextFileService extends Disposable implements ITex //#region revert async revert(resource: URI, options?: IRevertOptions): Promise { - return !(await this.revertAll([resource], options)).results.some(result => result.error); + return !(await this.doRevertAll([resource], options)).results.some(result => result.error); } - async revertAll(resources?: URI[], options?: IRevertOptions): Promise { + private async doRevertAll(resources?: URI[], options?: IRevertOptions): Promise { // Revert files first const revertOperationResult = await this.doRevertAllFiles(resources, options); @@ -739,18 +672,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex //#region dirty - getDirty(resources?: URI[]): URI[] { - - // Collect files - const dirty = this.getDirtyFileModels(resources).map(dirtyFileModel => dirtyFileModel.resource); - - // Add untitled ones - dirty.push(...this.untitledTextEditorService.getDirty(resources)); - - return dirty; - } - - isDirty(resource?: URI): boolean { + isDirty(resource: URI): boolean { // Check for dirty file if (this.models.getAll(resource).some(model => model.isDirty())) { @@ -761,5 +683,16 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return this.untitledTextEditorService.getDirty().some(dirty => !resource || dirty.toString() === resource.toString()); } + protected getDirty(resources?: URI[]): URI[] { + + // Collect files + const dirty = this.getDirtyFileModels(resources).map(dirtyFileModel => dirtyFileModel.resource); + + // Add untitled ones + dirty.push(...this.untitledTextEditorService.getDirty(resources)); + + return dirty; + } + //#endregion } diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 89ce41bdbaa..f505df3e772 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -45,18 +45,9 @@ export interface ITextFileService extends IDisposable { /** * A resource is dirty if it has unsaved changes or is an untitled file not yet saved. * - * @param resource the resource to check for being dirty. If it is not specified, will check for - * all dirty resources. + * @param resource the resource to check for being dirty */ - isDirty(resource?: URI): boolean; - - /** - * Returns all resources that are currently dirty matching the provided resources or all dirty resources. - * - * @param resources the resources to check for being dirty. If it is not specified, will check for - * all dirty resources. - */ - getDirty(resources?: URI[]): URI[]; + isDirty(resource: URI): boolean; /** * Saves the resource. @@ -77,15 +68,6 @@ export interface ITextFileService extends IDisposable { */ saveAs(resource: URI, targetResource?: URI, options?: ISaveOptions): Promise; - /** - * Saves the set of resources and returns a promise with the operation result. - * - * @param resources can be null to save all. - * @param includeUntitled to save all resources and optionally exclude untitled ones. - */ - saveAll(includeUntitled?: boolean, options?: ISaveOptions): Promise; - saveAll(resources: URI[], options?: ISaveOptions): Promise; - /** * Reverts the provided resource. * @@ -94,11 +76,6 @@ export interface ITextFileService extends IDisposable { */ revert(resource: URI, options?: IRevertOptions): Promise; - /** - * Reverts all the provided resources and returns a promise with the operation result. - */ - revertAll(resources?: URI[], options?: IRevertOptions): Promise; - /** * Create a file. If the file exists it will be overwritten with the contents if * the options enable to overwrite. diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index ccf2844c238..45c9082978b 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -386,7 +386,6 @@ suite('Files - TextFileEditorModel', () => { assert.ok(m1Mtime > 0); assert.ok(m2Mtime > 0); - assert.ok(accessor.textFileService.isDirty()); assert.ok(accessor.textFileService.isDirty(toResource.call(this, '/path/index_async2.txt'))); assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async.txt'))); @@ -394,7 +393,8 @@ suite('Files - TextFileEditorModel', () => { assert.ok(accessor.textFileService.isDirty(toResource.call(this, '/path/index_async.txt'))); await timeout(10); - await accessor.textFileService.saveAll(); + await accessor.textFileService.save(toResource.call(this, '/path/index_async.txt')); + await accessor.textFileService.save(toResource.call(this, '/path/index_async2.txt')); assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async.txt'))); assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async2.txt'))); assert.ok(assertIsDefined(model1.getStat()).mtime > m1Mtime); diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts index 4358e4dbc3f..e3d5a9b8e0a 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -60,42 +60,38 @@ suite('Files - TextFileService', () => { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.resource, model); - const service = accessor.textFileService; - await model.load(); - assert.ok(!service.isDirty(model.resource)); + assert.ok(!accessor.textFileService.isDirty(model.resource)); model.textEditorModel!.setValue('foo'); - assert.ok(service.isDirty(model.resource)); - assert.equal(service.getDirty().length, 1); - assert.equal(service.getDirty([model.resource])[0].toString(), model.resource.toString()); + assert.ok(accessor.textFileService.isDirty(model.resource)); + assert.equal(accessor.textFileService.getDirty().length, 1); + assert.equal(accessor.textFileService.getDirty([model.resource])[0].toString(), model.resource.toString()); const untitled = accessor.untitledTextEditorService.createOrGet(); const untitledModel = await untitled.resolve(); - assert.ok(!service.isDirty(untitled.getResource())); - assert.equal(service.getDirty().length, 1); + assert.ok(!accessor.textFileService.isDirty(untitled.getResource())); + assert.equal(accessor.textFileService.getDirty().length, 1); untitledModel.textEditorModel!.setValue('changed'); - assert.ok(service.isDirty(untitled.getResource())); - assert.equal(service.getDirty().length, 2); - assert.equal(service.getDirty([untitled.getResource()])[0].toString(), untitled.getResource().toString()); + assert.ok(accessor.textFileService.isDirty(untitled.getResource())); + assert.equal(accessor.textFileService.getDirty().length, 2); + assert.equal(accessor.textFileService.getDirty([untitled.getResource()])[0].toString(), untitled.getResource().toString()); }); test('save - file', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.resource, model); - const service = accessor.textFileService; - await model.load(); model.textEditorModel!.setValue('foo'); - assert.ok(service.isDirty(model.resource)); + assert.ok(accessor.textFileService.isDirty(model.resource)); - const res = await service.save(model.resource); + const res = await accessor.textFileService.save(model.resource); assert.ok(res); - assert.ok(!service.isDirty(model.resource)); + assert.ok(!accessor.textFileService.isDirty(model.resource)); }); test('save - UNC path', async function () { @@ -114,76 +110,62 @@ suite('Files - TextFileService', () => { await model.load(); model.textEditorModel!.setValue('foo'); - const res = await accessor.textFileService.saveAll(true); + const res = await accessor.textFileService.save(untitledUncUri); assert.ok(loadOrCreateStub.calledOnce); - assert.equal(res.results.length, 1); - assert.ok(!res.results[0].error); - assert.equal(res.results[0].target!.scheme, Schemas.file); - assert.equal(res.results[0].target!.authority, untitledUncUri.authority); - assert.equal(res.results[0].target!.path, untitledUncUri.path); + assert.ok(res); }); test('saveAll - file', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.resource, model); - const service = accessor.textFileService; - await model.load(); model.textEditorModel!.setValue('foo'); - assert.ok(service.isDirty(model.resource)); + assert.ok(accessor.textFileService.isDirty(model.resource)); - const res = await service.saveAll([model.resource]); + const res = await accessor.textFileService.save(model.resource); assert.ok(res); - assert.ok(!service.isDirty(model.resource)); - assert.equal(res.results.length, 1); - assert.equal(res.results[0].source.toString(), model.resource.toString()); + assert.ok(!accessor.textFileService.isDirty(model.resource)); }); test('saveAs - file', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.resource, model); - - const service = accessor.textFileService; - service.setPromptPath(model.resource); + accessor.textFileService.setPromptPath(model.resource); await model.load(); model.textEditorModel!.setValue('foo'); - assert.ok(service.isDirty(model.resource)); + assert.ok(accessor.textFileService.isDirty(model.resource)); - const res = await service.saveAs(model.resource); + const res = await accessor.textFileService.saveAs(model.resource); assert.equal(res!.toString(), model.resource.toString()); - assert.ok(!service.isDirty(model.resource)); + assert.ok(!accessor.textFileService.isDirty(model.resource)); }); test('revert - file', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.resource, model); - - const service = accessor.textFileService; - service.setPromptPath(model.resource); + accessor.textFileService.setPromptPath(model.resource); await model.load(); model!.textEditorModel!.setValue('foo'); - assert.ok(service.isDirty(model.resource)); + assert.ok(accessor.textFileService.isDirty(model.resource)); - const res = await service.revert(model.resource); + const res = await accessor.textFileService.revert(model.resource); assert.ok(res); - assert.ok(!service.isDirty(model.resource)); + assert.ok(!accessor.textFileService.isDirty(model.resource)); }); test('delete - dirty file', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.resource, model); - const service = accessor.textFileService; - await model.load(); model!.textEditorModel!.setValue('foo'); - assert.ok(service.isDirty(model.resource)); + assert.ok(accessor.textFileService.isDirty(model.resource)); - await service.delete(model.resource); - assert.ok(!service.isDirty(model.resource)); + await accessor.textFileService.delete(model.resource); + assert.ok(!accessor.textFileService.isDirty(model.resource)); }); test('move - dirty file', async function () { @@ -200,24 +182,22 @@ suite('Files - TextFileService', () => { (accessor.textFileService.models).add(sourceModel.resource, sourceModel); (accessor.textFileService.models).add(targetModel.resource, targetModel); - const service = accessor.textFileService; - await sourceModel.load(); sourceModel.textEditorModel!.setValue('foo'); - assert.ok(service.isDirty(sourceModel.resource)); + assert.ok(accessor.textFileService.isDirty(sourceModel.resource)); if (targetDirty) { await targetModel.load(); targetModel.textEditorModel!.setValue('bar'); - assert.ok(service.isDirty(targetModel.resource)); + assert.ok(accessor.textFileService.isDirty(targetModel.resource)); } - await service.move(sourceModel.resource, targetModel.resource, true); + await accessor.textFileService.move(sourceModel.resource, targetModel.resource, true); assert.equal(targetModel.textEditorModel!.getValue(), 'foo'); - assert.ok(!service.isDirty(sourceModel.resource)); - assert.ok(service.isDirty(targetModel.resource)); + assert.ok(!accessor.textFileService.isDirty(sourceModel.resource)); + assert.ok(accessor.textFileService.isDirty(targetModel.resource)); sourceModel.dispose(); targetModel.dispose(); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index 3193a3dd81c..5d7a8a95456 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -94,6 +94,8 @@ export interface IWorkingCopyService { readonly workingCopies: IWorkingCopy[]; + getWorkingCopies(resource: URI): IWorkingCopy[]; + registerWorkingCopy(workingCopy: IWorkingCopy): IDisposable; //#endregion @@ -127,6 +129,12 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic get workingCopies(): IWorkingCopy[] { return values(this._workingCopies); } private _workingCopies = new Set(); + getWorkingCopies(resource: URI): IWorkingCopy[] { + const workingCopies = this.mapResourceToWorkingCopy.get(resource.toString()); + + return workingCopies ? values(workingCopies) : []; + } + registerWorkingCopy(workingCopy: IWorkingCopy): IDisposable { const disposables = new DisposableStore(); diff --git a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts index a6a5269f462..435a48241f5 100644 --- a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts @@ -109,6 +109,8 @@ suite('WorkingCopyService', () => { assert.equal(service.dirtyCount, 1); assert.equal(service.dirtyWorkingCopies.length, 1); assert.equal(service.dirtyWorkingCopies[0], copy1); + assert.equal(service.getWorkingCopies(copy1.resource).length, 1); + assert.equal(service.getWorkingCopies(copy1.resource)[0], copy1); assert.equal(service.isDirty(resource1), true); assert.equal(service.hasDirty, true); assert.equal(onDidChangeDirty.length, 1); @@ -176,6 +178,10 @@ suite('WorkingCopyService', () => { const copy2 = new TestWorkingCopy(resource); const unregister2 = service.registerWorkingCopy(copy2); + assert.equal(service.getWorkingCopies(copy1.resource).length, 2); + assert.equal(service.getWorkingCopies(copy1.resource)[0], copy1); + assert.equal(service.getWorkingCopies(copy1.resource)[1], copy2); + copy1.setDirty(true); assert.equal(service.dirtyCount, 1); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index c048560e2d5..e437a2cc049 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -261,6 +261,10 @@ export class TestTextFileService extends NativeTextFileService { promptForPath(_resource: URI, _defaultPath: URI): Promise { return Promise.resolve(this.promptPath); } + + getDirty(resources?: URI[]): URI[] { + return super.getDirty(resources); + } } export interface ITestInstantiationService extends IInstantiationService { From b2950b03a8ea046553dd3e85071acddc24da3a8b Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 15 Jan 2020 10:48:51 +0100 Subject: [PATCH 307/843] Adopt css --- extensions/css-language-features/client/src/cssMain.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index 8822571d076..81a5f7df086 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -63,12 +63,12 @@ export function activate(context: ExtensionContext) { r.items.forEach(i => { if (i.kind === CompletionItemKind.Color) { i.label = { - label: i.label as string, + name: i.label as string, details: i.documentation?.toString() }; } else { i.label = { - label: i.label as string, + name: i.label as string, details: i.label as string }; } From 7f8018fbf217914a6776af60e025eb22db1072e6 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 15 Jan 2020 11:05:59 +0100 Subject: [PATCH 308/843] Try adoptions --- .../css-language-features/server/src/pathCompletion.ts | 6 ++++++ extensions/html-language-features/client/src/htmlMain.ts | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/extensions/css-language-features/server/src/pathCompletion.ts b/extensions/css-language-features/server/src/pathCompletion.ts index 6862f28e034..d480da4f350 100644 --- a/extensions/css-language-features/server/src/pathCompletion.ts +++ b/extensions/css-language-features/server/src/pathCompletion.ts @@ -54,6 +54,12 @@ export function getPathCompletionParticipant( }); } + if (suggestions[0]) { + suggestions[0].documentation = 'test: ' + suggestions[0].label; + } + if (suggestions[2]) { + suggestions[2].documentation = 'test: ' + suggestions[2].label; + } result.items = [...suggestions, ...result.items]; } }; diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index 2df5e7f5c13..923477f51be 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -104,6 +104,12 @@ export function activate(context: ExtensionContext) { function updateProposals(r: CompletionItem[] | CompletionList | null | undefined): CompletionItem[] | CompletionList | null | undefined { if (r) { (Array.isArray(r) ? r : r.items).forEach(updateRanges); + + if (!Array.isArray(r)) { + r.items.forEach(i => { + i.detail = typeof i.label === 'string' ? i.label : i.label.name; + }); + } } return r; } From 5c928dff37ab5b5c9de036df79f1c3a580c30f42 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 11:05:12 +0100 Subject: [PATCH 309/843] Fix #85618 --- .../sharedProcess/sharedProcessMain.ts | 8 +++- .../userDataSync/common/extensionsSync.ts | 6 +++ .../userDataSync/common/globalStateSync.ts | 6 +++ .../userDataSync/common/keybindingsSync.ts | 6 +++ .../userDataSync/common/settingsSync.ts | 6 +++ .../userDataSync/common/userDataAutoSync.ts | 41 ++++++++++++++----- .../userDataSync/common/userDataSync.ts | 11 +++++ .../userDataSync/common/userDataSyncIpc.ts | 26 +++++++++++- .../common/userDataSyncService.ts | 37 +++++++++++++++++ .../common/userDataSyncStoreService.ts | 15 +++++++ .../electron-browser/userDataAutoSync.ts | 9 ++-- .../test/common/keybindingsMerge.test.ts | 2 + .../userDataSync/browser/userDataAutoSync.ts | 7 ++-- .../userDataSync/browser/userDataSync.ts | 33 +++++++++------ .../userDataSync/common/userDataSyncUtil.ts | 6 +++ .../electron-browser/settingsSyncService.ts | 4 ++ .../userDataAutoSyncService.ts | 31 ++++++++++++++ .../electron-browser/userDataSyncService.ts | 8 ++++ src/vs/workbench/workbench.desktop.main.ts | 1 + 19 files changed, 231 insertions(+), 32 deletions(-) create mode 100644 src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 4db9464573e..0302d64755b 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -53,7 +53,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; -import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel, UserDataAuthTokenServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; +import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel, UserDataAuthTokenServiceChannel, UserDataAutoSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; @@ -217,6 +217,10 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService); server.registerChannel('userDataSync', userDataSyncChannel); + const userDataAutoSync = instantiationService2.createInstance(UserDataAutoSync); + const userDataAutoSyncChannel = new UserDataAutoSyncChannel(userDataAutoSync); + server.registerChannel('userDataAutoSync', userDataAutoSyncChannel); + // clean up deprecated extensions (extensionManagementService as ExtensionManagementService).removeDeprecatedExtensions(); // update localizations cache @@ -227,7 +231,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat instantiationService2.createInstance(LanguagePackCachedDataCleaner), instantiationService2.createInstance(StorageDataCleaner), instantiationService2.createInstance(LogsDataCleaner), - instantiationService2.createInstance(UserDataAutoSync) + userDataAutoSync )); disposables.add(extensionManagementService as ExtensionManagementService); }); diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index d97262ec02c..39e6fada327 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -192,6 +192,12 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser }); } + async resetLocal(): Promise { + try { + await this.fileService.del(this.lastSyncExtensionsResource); + } catch (e) { /* ignore */ } + } + private async getPreview(): Promise { const lastSyncData = await this.getLastSyncUserData(); const lastSyncExtensions: ISyncExtension[] | null = lastSyncData ? JSON.parse(lastSyncData.content!) : null; diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 1baefb1c24e..64765b828b3 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -155,6 +155,12 @@ export class GlobalStateSynchroniser extends Disposable implements ISynchroniser return remoteUserData.content !== null; } + async resetLocal(): Promise { + try { + await this.fileService.del(this.lastSyncGlobalStateResource); + } catch (e) { /* ignore */ } + } + private async getPreview(): Promise { const lastSyncData = await this.getLastSyncUserData(); const lastSyncGlobalState = lastSyncData && lastSyncData.content ? JSON.parse(lastSyncData.content) : null; diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 3167c18320f..cac2ea5826c 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -219,6 +219,12 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser return remoteUserData.content !== null; } + async resetLocal(): Promise { + try { + await this.fileService.del(this.lastSyncKeybindingsResource); + } catch (e) { /* ignore */ } + } + private async continueSync(): Promise { if (this.status !== SyncStatus.HasConflicts) { return false; diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 203743d2b7c..f0723de37a6 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -220,6 +220,12 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer } } + async resetLocal(): Promise { + try { + await this.fileService.del(this.lastSyncSettingsResource); + } catch (e) { /* ignore */ } + } + private async doSync(resolvedConflicts: { key: string, value: any | undefined }[]): Promise { try { const result = await this.getPreview(resolvedConflicts); diff --git a/src/vs/platform/userDataSync/common/userDataAutoSync.ts b/src/vs/platform/userDataSync/common/userDataAutoSync.ts index e6a3d612dee..84be425ad53 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSync.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSync.ts @@ -7,9 +7,11 @@ import { timeout } from 'vs/base/common/async'; import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IUserDataSyncLogService, IUserDataSyncService, SyncStatus, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncLogService, IUserDataSyncService, SyncStatus, IUserDataAuthTokenService, IUserDataAutoSyncService, IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; -export class UserDataAutoSync extends Disposable { +export class UserDataAutoSync extends Disposable implements IUserDataAutoSyncService { + + _serviceBrand: any; private enabled: boolean = false; @@ -18,15 +20,16 @@ export class UserDataAutoSync extends Disposable { @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @IUserDataAuthTokenService private readonly userDataAuthTokenService: IUserDataAuthTokenService, + @IUserDataSyncUtilService private readonly userDataSyncUtilService: IUserDataSyncUtilService, ) { super(); - this.updateEnablement(false); - this._register(Event.any(userDataAuthTokenService.onDidChangeToken)(() => this.updateEnablement(true))); - this._register(Event.any(userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true))); - this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('sync.enable'))(() => this.updateEnablement(true))); + this.updateEnablement(false, true); + this._register(Event.any(userDataAuthTokenService.onDidChangeToken)(() => this.updateEnablement(true, true))); + this._register(Event.any(userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true, true))); + this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('sync.enable'))(() => this.updateEnablement(true, false))); } - private async updateEnablement(stopIfDisabled: boolean): Promise { + private async updateEnablement(stopIfDisabled: boolean, auto: boolean): Promise { const enabled = await this.isSyncEnabled(); if (this.enabled === enabled) { return; @@ -35,7 +38,7 @@ export class UserDataAutoSync extends Disposable { this.enabled = enabled; if (this.enabled) { this.logService.info('Syncing configuration started'); - this.sync(true); + this.sync(true, auto); return; } else { if (stopIfDisabled) { @@ -46,24 +49,42 @@ export class UserDataAutoSync extends Disposable { } - protected async sync(loop: boolean): Promise { + private async sync(loop: boolean, auto: boolean): Promise { if (this.enabled) { try { + if (auto) { + if (await this.isTurnedOffEverywhere()) { + // Turned off everywhere. Reset & Stop Sync. + await this.userDataSyncService.resetLocal(); + await this.userDataSyncUtilService.updateConfigurationValue('sync.enable', false); + return; + } + } await this.userDataSyncService.sync(); } catch (e) { this.logService.error(e); } if (loop) { await timeout(1000 * 60 * 5); // Loop sync for every 5 min. - this.sync(loop); + this.sync(loop, true); } } } + private async isTurnedOffEverywhere(): Promise { + const hasRemote = await this.userDataSyncService.hasRemote(); + const hasPreviouslySynced = await this.userDataSyncService.hasPreviouslySynced(); + return !hasRemote && hasPreviouslySynced; + } + private async isSyncEnabled(): Promise { return this.configurationService.getValue('sync.enable') && this.userDataSyncService.status !== SyncStatus.Uninitialized && !!(await this.userDataAuthTokenService.getToken()); } + triggerAutoSync(): Promise { + return this.sync(false, true); + } + } diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 71c11fdd645..c58a823060f 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -153,6 +153,7 @@ export interface IUserDataSyncStoreService { readonly userDataSyncStore: IUserDataSyncStore | undefined; read(key: string, oldValue: IUserData | null): Promise; write(key: string, content: string, ref: string | null): Promise; + clear(): Promise; } export interface ISyncExtension { @@ -190,18 +191,28 @@ export interface ISynchroniser { stop(): void; hasPreviouslySynced(): Promise hasRemote(): Promise; + resetLocal(): Promise; } export const IUserDataSyncService = createDecorator('IUserDataSyncService'); export interface IUserDataSyncService extends ISynchroniser { _serviceBrand: any; readonly conflictsSource: SyncSource | null; + reset(): Promise; + resetLocal(): Promise; removeExtension(identifier: IExtensionIdentifier): Promise; } +export const IUserDataAutoSyncService = createDecorator('IUserDataAutoSyncService'); +export interface IUserDataAutoSyncService { + _serviceBrand: any; + triggerAutoSync(): Promise; +} + export const IUserDataSyncUtilService = createDecorator('IUserDataSyncUtilService'); export interface IUserDataSyncUtilService { _serviceBrand: undefined; + updateConfigurationValue(key: string, value: any): Promise; resolveUserBindings(userbindings: string[]): Promise>; resolveFormattingOptions(resource: URI): Promise; } diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index 8e647cfef99..78db0449fc1 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -5,7 +5,7 @@ import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; -import { IUserDataSyncService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAuthTokenService, IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; import { URI } from 'vs/base/common/uri'; import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; @@ -31,6 +31,8 @@ export class UserDataSyncChannel implements IServerChannel { case 'getConflictsSource': return Promise.resolve(this.service.conflictsSource); case 'removeExtension': return this.service.removeExtension(args[0]); case 'stop': this.service.stop(); return Promise.resolve(); + case 'reset': return this.service.reset(); + case 'resetLocal': return this.service.resetLocal(); case 'hasPreviouslySynced': return this.service.hasPreviouslySynced(); case 'hasRemote': return this.service.hasRemote(); } @@ -59,6 +61,7 @@ export class SettingsSyncChannel implements IServerChannel { case '_getInitialStatus': return Promise.resolve(this.service.status); case '_getInitialConflicts': return Promise.resolve(this.service.conflicts); case 'stop': this.service.stop(); return Promise.resolve(); + case 'resetLocal': return this.service.resetLocal(); case 'hasPreviouslySynced': return this.service.hasPreviouslySynced(); case 'hasRemote': return this.service.hasRemote(); case 'resolveConflicts': return this.service.resolveConflicts(args[0]); @@ -67,6 +70,22 @@ export class SettingsSyncChannel implements IServerChannel { } } +export class UserDataAutoSyncChannel implements IServerChannel { + + constructor(private readonly service: IUserDataAutoSyncService) { } + + listen(_: unknown, event: string): Event { + throw new Error(`Event not found: ${event}`); + } + + call(context: any, command: string, args?: any): Promise { + switch (command) { + case 'triggerAutoSync': return this.service.triggerAutoSync(); + } + throw new Error('Invalid call'); + } +} + export class UserDataAuthTokenServiceChannel implements IServerChannel { constructor(private readonly service: IUserDataAuthTokenService) { } @@ -98,6 +117,7 @@ export class UserDataSycnUtilServiceChannel implements IServerChannel { switch (command) { case 'resolveUserKeybindings': return this.service.resolveUserBindings(args[0]); case 'resolveFormattingOptions': return this.service.resolveFormattingOptions(URI.revive(args[0])); + case 'updateConfigurationValue': return this.service.updateConfigurationValue(args[0], args[1]); } throw new Error('Invalid call'); } @@ -118,5 +138,9 @@ export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService { return this.channel.call('resolveFormattingOptions', [file]); } + async updateConfigurationValue(key: string, value: any): Promise { + return this.channel.call('updateConfigurationValue', [key, value]); + } + } diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 84102d532bd..d2999331cf7 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -146,6 +146,43 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return false; } + async reset(): Promise { + await this.resetRemote(); + await this.resetLocal(); + } + + private async resetRemote(): Promise { + if (!this.userDataSyncStoreService.userDataSyncStore) { + throw new Error('Not enabled'); + } + if (!(await this.userDataAuthTokenService.getToken())) { + throw new Error('Not Authenticated. Please sign in to start sync.'); + } + try { + await this.userDataSyncStoreService.clear(); + this.logService.info('Completed clearing remote data'); + } catch (e) { + this.logService.error(e); + } + } + + async resetLocal(): Promise { + if (!this.userDataSyncStoreService.userDataSyncStore) { + throw new Error('Not enabled'); + } + if (!(await this.userDataAuthTokenService.getToken())) { + throw new Error('Not Authenticated. Please sign in to start sync.'); + } + for (const synchroniser of this.synchronisers) { + try { + await synchroniser.resetLocal(); + } catch (e) { + this.logService.error(`${this.getSyncSource(synchroniser)}: ${toErrorMessage(e)}`); + } + } + this.logService.info('Completed resetting local cache'); + } + removeExtension(identifier: IExtensionIdentifier): Promise { return this.extensionsSynchroniser.removeExtension(identifier); } diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index df5094c426b..f33c1880600 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -88,6 +88,21 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn return newRef; } + async clear(): Promise { + if (!this.userDataSyncStore) { + throw new Error('No settings sync store url configured.'); + } + + const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource').toString(); + const headers: IHeaders = { 'Content-Type': 'text/plain' }; + + const context = await this.request({ type: 'DELETE', url, headers }, CancellationToken.None); + + if (!isSuccess(context)) { + throw new Error('Server returned ' + context.res.statusCode); + } + } + private async request(options: IRequestOptions, token: CancellationToken): Promise { const authToken = await this.authTokenService.getToken(); if (!authToken) { diff --git a/src/vs/platform/userDataSync/electron-browser/userDataAutoSync.ts b/src/vs/platform/userDataSync/electron-browser/userDataAutoSync.ts index 821d8a05ed5..6ca2e9085e3 100644 --- a/src/vs/platform/userDataSync/electron-browser/userDataAutoSync.ts +++ b/src/vs/platform/userDataSync/electron-browser/userDataAutoSync.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, IUserDataSyncLogService, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncLogService, IUserDataAuthTokenService, IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; import { Event } from 'vs/base/common/event'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { UserDataAutoSync as BaseUserDataAutoSync } from 'vs/platform/userDataSync/common/userDataAutoSync'; @@ -17,15 +17,16 @@ export class UserDataAutoSync extends BaseUserDataAutoSync { @IConfigurationService configurationService: IConfigurationService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IUserDataAuthTokenService authTokenService: IUserDataAuthTokenService, + @IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService, ) { - super(configurationService, userDataSyncService, logService, authTokenService); + super(configurationService, userDataSyncService, logService, authTokenService, userDataSyncUtilService); // Sync immediately if there is a local change. this._register(Event.debounce(Event.any( electronService.onWindowFocus, electronService.onWindowOpen, - userDataSyncService.onDidChangeLocal - ), () => undefined, 500)(() => this.sync(false))); + userDataSyncService.onDidChangeLocal, + ), () => undefined, 500)(() => this.triggerAutoSync())); } } diff --git a/src/vs/platform/userDataSync/test/common/keybindingsMerge.test.ts b/src/vs/platform/userDataSync/test/common/keybindingsMerge.test.ts index a1b1a5d696d..c4d3a40d5ba 100644 --- a/src/vs/platform/userDataSync/test/common/keybindingsMerge.test.ts +++ b/src/vs/platform/userDataSync/test/common/keybindingsMerge.test.ts @@ -731,4 +731,6 @@ class MockUserDataSyncUtilService implements IUserDataSyncUtilService { async resolveFormattingOptions(file?: URI): Promise { return { eol: '\n', insertSpaces: false, tabSize: 4 }; } + + async updateConfigurationValue(key: string, value: any): Promise { } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSync.ts index 31ec8de3166..2fd6b1ea03b 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSync.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, IUserDataSyncLogService, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncLogService, IUserDataAuthTokenService, IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Event } from 'vs/base/common/event'; import { UserDataAutoSync as BaseUserDataAutoSync } from 'vs/platform/userDataSync/common/userDataAutoSync'; @@ -20,15 +20,16 @@ export class UserDataAutoSync extends BaseUserDataAutoSync { @IUserDataAuthTokenService authTokenService: IUserDataAuthTokenService, @IInstantiationService instantiationService: IInstantiationService, @IHostService hostService: IHostService, + @IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService, ) { - super(configurationService, userDataSyncService, logService, authTokenService); + super(configurationService, userDataSyncService, logService, authTokenService, userDataSyncUtilService); // Sync immediately if there is a local change. this._register(Event.debounce(Event.any( userDataSyncService.onDidChangeLocal, instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync, hostService.onDidChangeFocus - ), () => undefined, 500)(() => this.sync(false))); + ), () => undefined, 500)(() => this.triggerAutoSync())); } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index e91b707a36c..1a03b92a166 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, IUserDataSyncStore, registerConfiguration, getUserDataSyncStore, ISyncConfiguration, IUserDataAuthTokenService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, IUserDataSyncStore, registerConfiguration, getUserDataSyncStore, ISyncConfiguration, IUserDataAuthTokenService, IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; import { localize } from 'vs/nls'; import { Disposable, MutableDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -76,6 +76,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @IInstantiationService instantiationService: IInstantiationService, @IOutputService private readonly outputService: IOutputService, @IUserDataAuthTokenService private readonly userDataAuthTokenService: IUserDataAuthTokenService, + @IUserDataAutoSyncService userDataAutoSyncService: IUserDataAutoSyncService, ) { super(); this.userDataSyncStore = getUserDataSyncStore(configurationService); @@ -94,20 +95,12 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (isWeb) { this._register(instantiationService.createInstance(UserDataAutoSync)); } else { - this._register(instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync(() => this.triggerSync())); + this._register(instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync(() => userDataAutoSyncService.triggerAutoSync())); } }); } } - private triggerSync(): void { - if (this.configurationService.getValue('sync.enable') - && this.userDataSyncService.status !== SyncStatus.Uninitialized - && this.authenticationState.get() === MSAAuthStatus.SignedIn) { - this.userDataSyncService.sync(); - } - } - private async initializeActiveAccount(): Promise { const accounts = await this.authenticationService.getAccounts(MSA); // MSA provider has not yet been registered @@ -441,7 +434,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo case 1: return Promise.reject(canceled()); case 2: return this.userDataSyncService.pull(); // case 3: return this.userDataSyncService.push(); - case 3: return this.notificationService.info('not yet supported'); + case 3: return this.notificationService.info('Upload: Not yet supported'); } } } @@ -451,10 +444,16 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo type: 'info', message: localize('turn off sync confirmation', "Turn off Sync"), detail: localize('turn off sync detail', "Your settings, keybindings, extensions and more will no longer be synced."), - primaryButton: localize('turn off', "Turn off") + primaryButton: localize('turn off', "Turn off"), + checkbox: { + label: localize('turn off sync everywhere', "Turn off sync in all your devices and clear the data from cloud.") + } }); if (result.confirmed) { await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, false); + if (result.checkboxChecked) { + await this.userDataSyncService.reset(); + } } } @@ -689,5 +688,15 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }, when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)), }); + + const resetLocalCommandId = 'workbench.userData.actions.resetLocal'; + CommandsRegistry.registerCommand(resetLocalCommandId, () => this.userDataSyncService.resetLocal()); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: resetLocalCommandId, + title: localize('reset local', "Developer: Reset Local (Sync)") + }, + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)), + }); } } diff --git a/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts b/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts index c4ee5454269..94ab9023ff7 100644 --- a/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts +++ b/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts @@ -11,6 +11,7 @@ import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { URI } from 'vs/base/common/uri'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ITextResourcePropertiesService, ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; class UserDataSyncUtilService implements IUserDataSyncUtilService { @@ -21,8 +22,13 @@ class UserDataSyncUtilService implements IUserDataSyncUtilService { @ITextModelService private readonly textModelService: ITextModelService, @ITextResourcePropertiesService private readonly textResourcePropertiesService: ITextResourcePropertiesService, @ITextResourceConfigurationService private readonly textResourceConfigurationService: ITextResourceConfigurationService, + @IConfigurationService private readonly configurationService: IConfigurationService, ) { } + public async updateConfigurationValue(key: string, value: any): Promise { + await this.configurationService.updateValue(key, value, ConfigurationTarget.USER); + } + public async resolveUserBindings(userBindings: string[]): Promise> { const keys: IStringDictionary = {}; for (const userbinding of userBindings) { diff --git a/src/vs/workbench/services/userDataSync/electron-browser/settingsSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/settingsSyncService.ts index 345482f53b1..ef560109609 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/settingsSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/settingsSyncService.ts @@ -61,6 +61,10 @@ export class SettingsSyncService extends Disposable implements ISettingsSyncServ this.channel.call('stop'); } + resetLocal(): Promise { + return this.channel.call('resetLocal'); + } + hasPreviouslySynced(): Promise { return this.channel.call('hasPreviouslySynced'); } diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts new file mode 100644 index 00000000000..f773ebf8be1 --- /dev/null +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +export class UserDataAutoSyncService extends Disposable implements IUserDataAutoSyncService { + + _serviceBrand: undefined; + + private readonly channel: IChannel; + + constructor( + @ISharedProcessService sharedProcessService: ISharedProcessService + ) { + super(); + this.channel = sharedProcessService.getChannel('userDataAutoSync'); + } + + triggerAutoSync(): Promise { + return this.channel.call('triggerAutoSync'); + } + +} + +registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService); diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts index a8b04ea9302..85c0ec1271c 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts @@ -50,6 +50,14 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return this.channel.call('sync', [_continue]); } + reset(): Promise { + return this.channel.call('reset'); + } + + resetLocal(): Promise { + return this.channel.call('resetLocal'); + } + stop(): void { this.channel.call('stop'); } diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index f9656900129..c0bee3d2971 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -51,6 +51,7 @@ import 'vs/workbench/services/workspaces/electron-browser/workspacesService'; import 'vs/workbench/services/workspaces/electron-browser/workspaceEditingService'; import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncService'; import 'vs/workbench/services/userDataSync/electron-browser/settingsSyncService'; +import 'vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService'; import 'vs/workbench/services/userDataSync/electron-browser/userDataAuthTokenService'; import 'vs/workbench/services/authentication/browser/authenticationService'; import 'vs/workbench/services/host/electron-browser/desktopHostService'; From 5bfd23cdd84ec7e405943cf852fa7e4700631a2f Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 15 Jan 2020 11:07:24 +0100 Subject: [PATCH 310/843] Accessibility navigation commands should only be enabled on windows since they are tuned to what NVDA expects fixes #87665 --- .../editor/contrib/wordOperations/wordOperations.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index 5e77d1c0607..57d9174719d 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -172,6 +172,7 @@ export class CursorWordLeftSelect extends WordLeftCommand { } } +// Accessibility navigation commands should only be enabled on windows since they are tuned to what NVDA expects export class CursorWordAccessibilityLeft extends WordLeftCommand { constructor() { super({ @@ -181,8 +182,7 @@ export class CursorWordAccessibilityLeft extends WordLeftCommand { precondition: undefined, kbOpts: { kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), - primary: KeyMod.CtrlCmd | KeyCode.LeftArrow, - mac: { primary: KeyMod.Alt | KeyCode.LeftArrow }, + win: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow }, weight: KeybindingWeight.EditorContrib + 1 } }); @@ -202,8 +202,7 @@ export class CursorWordAccessibilityLeftSelect extends WordLeftCommand { precondition: undefined, kbOpts: { kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow, - mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow }, + win: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow }, weight: KeybindingWeight.EditorContrib + 1 } }); @@ -301,8 +300,7 @@ export class CursorWordAccessibilityRight extends WordRightCommand { precondition: undefined, kbOpts: { kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), - primary: KeyMod.CtrlCmd | KeyCode.RightArrow, - mac: { primary: KeyMod.Alt | KeyCode.RightArrow }, + win: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow }, weight: KeybindingWeight.EditorContrib + 1 } }); @@ -322,8 +320,7 @@ export class CursorWordAccessibilityRightSelect extends WordRightCommand { precondition: undefined, kbOpts: { kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow, - mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow }, + win: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow }, weight: KeybindingWeight.EditorContrib + 1 } }); From ad7fd5c516837f66577b426c2c97127921f41f3d Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 15 Jan 2020 11:08:48 +0100 Subject: [PATCH 311/843] Drop alwaysRevealInlineDetails --- src/vs/editor/common/config/editorOptions.ts | 11 ----------- src/vs/editor/contrib/suggest/suggestWidget.ts | 3 --- .../contrib/suggest/test/completionModel.test.ts | 1 - src/vs/monaco.d.ts | 4 ---- 4 files changed, 19 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index feb53271431..104c3a91d58 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2495,10 +2495,6 @@ export interface ISuggestOptions { * Max suggestions to show in suggestions. Defaults to 12. */ maxVisibleSuggestions?: number; - /** - * Always show inline details - */ - alwaysRevealInlineDetails?: boolean; /** * Show method-suggestions. */ @@ -2618,7 +2614,6 @@ class EditorSuggest extends BaseEditorOption toggleClass(this.element, 'no-icons', !this.editor.getOption(EditorOption.suggest).showIcons); - const applyAlwaysRevealInlineDetailsStyle = () => toggleClass(this.element, 'always-reveal-inline-details', this.editor.getOption(EditorOption.suggest).alwaysRevealInlineDetails); applyIconStyle(); - applyAlwaysRevealInlineDetailsStyle(); let renderer = instantiationService.createInstance(Renderer, this, this.editor, triggerKeybindingLabel); @@ -552,7 +550,6 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate { if (e.hasChanged(EditorOption.suggest)) { applyIconStyle(); - applyAlwaysRevealInlineDetailsStyle(); } })); diff --git a/src/vs/editor/contrib/suggest/test/completionModel.test.ts b/src/vs/editor/contrib/suggest/test/completionModel.test.ts index c3bd3d76100..6a26c5fd584 100644 --- a/src/vs/editor/contrib/suggest/test/completionModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/completionModel.test.ts @@ -41,7 +41,6 @@ suite('CompletionModel', function () { shareSuggestSelections: false, showIcons: true, maxVisibleSuggestions: 12, - alwaysRevealInlineDetails: false, showMethods: true, showFunctions: true, showConstructors: true, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index c463fa8414e..226176b9410 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3381,10 +3381,6 @@ declare namespace monaco.editor { * Max suggestions to show in suggestions. Defaults to 12. */ maxVisibleSuggestions?: number; - /** - * Always show inline details - */ - alwaysRevealInlineDetails?: boolean; /** * Show method-suggestions. */ From 7cfc1943529a064f9986f99a6bf2f1c4e01e4a1c Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 15 Jan 2020 11:12:55 +0100 Subject: [PATCH 312/843] Fixes #85712 --- .../editor/common/commands/replaceCommand.ts | 6 +- src/vs/editor/common/controller/cursor.ts | 10 +- .../common/controller/cursorTypeOperations.ts | 2 +- src/vs/editor/common/editorCommon.ts | 4 +- .../test/linesOperations.test.ts | 304 +++++++++--------- .../browser/commands/shiftCommand.test.ts | 3 +- .../trimTrailingWhitespaceCommand.test.ts | 3 +- .../test/browser/controller/cursor.test.ts | 26 +- src/vs/editor/test/browser/testCommand.ts | 10 +- src/vs/monaco.d.ts | 4 +- 10 files changed, 206 insertions(+), 166 deletions(-) diff --git a/src/vs/editor/common/commands/replaceCommand.ts b/src/vs/editor/common/commands/replaceCommand.ts index 4ae44cf28c1..3e628f2db76 100644 --- a/src/vs/editor/common/commands/replaceCommand.ts +++ b/src/vs/editor/common/commands/replaceCommand.ts @@ -122,17 +122,19 @@ export class ReplaceCommandThatPreservesSelection implements ICommand { private readonly _range: Range; private readonly _text: string; private readonly _initialSelection: Selection; + private readonly _forceMoveMarkers: boolean; private _selectionId: string | null; - constructor(editRange: Range, text: string, initialSelection: Selection) { + constructor(editRange: Range, text: string, initialSelection: Selection, forceMoveMarkers: boolean = false) { this._range = editRange; this._text = text; this._initialSelection = initialSelection; + this._forceMoveMarkers = forceMoveMarkers; this._selectionId = null; } public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { - builder.addEditOperation(this._range, this._text); + builder.addTrackedEditOperation(this._range, this._text, this._forceMoveMarkers); this._selectionId = builder.trackSelection(this._initialSelection); } diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 80e20c21d7f..79fa0e5ff5b 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -727,7 +727,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { case H.Paste: cursorChangeReason = CursorChangeReason.Paste; - this._paste(payload.text, payload.pasteOnNewLine, payload.multicursorText); + this._paste(payload.text, payload.pasteOnNewLine, payload.multicursorText || []); break; case H.Cut: @@ -1000,7 +1000,7 @@ class CommandExecutor { let operations: IIdentifiedSingleEditOperation[] = []; let operationMinor = 0; - const addEditOperation = (selection: Range, text: string | null) => { + const addEditOperation = (selection: Range, text: string | null, forceMoveMarkers: boolean = false) => { if (selection.isEmpty() && text === '') { // This command wants to add a no-op => no thank you return; @@ -1012,15 +1012,15 @@ class CommandExecutor { }, range: selection, text: text, - forceMoveMarkers: false, + forceMoveMarkers: forceMoveMarkers, isAutoWhitespaceEdit: command.insertsAutoWhitespace }); }; let hadTrackedEditOperation = false; - const addTrackedEditOperation = (selection: Range, text: string | null) => { + const addTrackedEditOperation = (selection: Range, text: string | null, forceMoveMarkers?: boolean) => { hadTrackedEditOperation = true; - addEditOperation(selection, text); + addEditOperation(selection, text, forceMoveMarkers); }; const trackSelection = (selection: Selection, trackPreviousOnEmpty?: boolean) => { diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index d9ef5f66191..aab378176b7 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -94,7 +94,7 @@ export class TypeOperations { if (pasteOnNewLine) { // Paste entire line at the beginning of line let typeSelection = new Range(position.lineNumber, 1, position.lineNumber, 1); - commands[i] = new ReplaceCommandThatPreservesSelection(typeSelection, text, selection); + commands[i] = new ReplaceCommandThatPreservesSelection(typeSelection, text, selection, true); } else { commands[i] = new ReplaceCommand(selection, text); } diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 679cb676023..946d24472a8 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -22,7 +22,7 @@ export interface IEditOperationBuilder { * @param range The range to replace (delete). May be empty to represent a simple insert. * @param text The text to replace with. May be null to represent a simple delete. */ - addEditOperation(range: Range, text: string | null): void; + addEditOperation(range: Range, text: string | null, forceMoveMarkers?: boolean): void; /** * Add a new edit operation (a replace operation). @@ -30,7 +30,7 @@ export interface IEditOperationBuilder { * @param range The range to replace (delete). May be empty to represent a simple insert. * @param text The text to replace with. May be null to represent a simple delete. */ - addTrackedEditOperation(range: Range, text: string | null): void; + addTrackedEditOperation(range: Range, text: string | null, forceMoveMarkers?: boolean): void; /** * Track `selection` when applying edit operations. diff --git a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts index 58663215088..64b26b23b76 100644 --- a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts @@ -12,6 +12,19 @@ import { ITextModel } from 'vs/editor/common/model'; import { TitleCaseAction, DeleteAllLeftAction, DeleteAllRightAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, LowerCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TransposeAction, UpperCaseAction, DeleteLinesAction } from 'vs/editor/contrib/linesOperations/linesOperations'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; +import type { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction } from 'vs/editor/browser/editorExtensions'; + +function assertSelection(editor: ICodeEditor, expected: Selection | Selection[]): void { + if (!Array.isArray(expected)) { + expected = [expected]; + } + assert.deepEqual(editor.getSelections(), expected); +} + +function executeAction(action: EditorAction, editor: ICodeEditor): void { + action.run(null!, editor, undefined); +} suite('Editor Contrib - Line Operations', () => { suite('SortLinesAscendingAction', () => { @@ -26,13 +39,13 @@ suite('Editor Contrib - Line Operations', () => { let sortLinesAscendingAction = new SortLinesAscendingAction(); editor.setSelection(new Selection(1, 1, 3, 5)); - sortLinesAscendingAction.run(null!, editor); + executeAction(sortLinesAscendingAction, editor); assert.deepEqual(model.getLinesContent(), [ 'alpha', 'beta', 'omicron' ]); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 3, 7).toString()); + assertSelection(editor, new Selection(1, 1, 3, 7)); }); }); @@ -51,7 +64,7 @@ suite('Editor Contrib - Line Operations', () => { let sortLinesAscendingAction = new SortLinesAscendingAction(); editor.setSelections([new Selection(1, 1, 3, 5), new Selection(5, 1, 7, 5)]); - sortLinesAscendingAction.run(null!, editor); + executeAction(sortLinesAscendingAction, editor); assert.deepEqual(model.getLinesContent(), [ 'alpha', 'beta', @@ -84,13 +97,13 @@ suite('Editor Contrib - Line Operations', () => { let sortLinesDescendingAction = new SortLinesDescendingAction(); editor.setSelection(new Selection(1, 1, 3, 7)); - sortLinesDescendingAction.run(null!, editor); + executeAction(sortLinesDescendingAction, editor); assert.deepEqual(model.getLinesContent(), [ 'omicron', 'beta', 'alpha' ]); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 3, 5).toString()); + assertSelection(editor, new Selection(1, 1, 3, 5)); }); }); @@ -109,7 +122,7 @@ suite('Editor Contrib - Line Operations', () => { let sortLinesDescendingAction = new SortLinesDescendingAction(); editor.setSelections([new Selection(1, 1, 3, 7), new Selection(5, 1, 7, 7)]); - sortLinesDescendingAction.run(null!, editor); + executeAction(sortLinesDescendingAction, editor); assert.deepEqual(model.getLinesContent(), [ 'omicron', 'beta', @@ -143,13 +156,13 @@ suite('Editor Contrib - Line Operations', () => { let deleteAllLeftAction = new DeleteAllLeftAction(); editor.setSelection(new Selection(1, 2, 1, 2)); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'ne', '001'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(1), 'ne'); editor.setSelections([new Selection(2, 2, 2, 2), new Selection(3, 2, 3, 2)]); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(2), 'wo', '002'); - assert.equal(model.getLineContent(3), 'hree', '003'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(2), 'wo'); + assert.equal(model.getLineContent(3), 'hree'); }); }); @@ -164,16 +177,16 @@ suite('Editor Contrib - Line Operations', () => { let deleteAllLeftAction = new DeleteAllLeftAction(); editor.setSelection(new Selection(2, 1, 2, 1)); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'onetwo', '001'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(1), 'onetwo'); editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]); - deleteAllLeftAction.run(null!, editor); + executeAction(deleteAllLeftAction, editor); assert.equal(model.getLinesContent()[0], 'onetwothree'); assert.equal(model.getLinesContent().length, 1); editor.setSelection(new Selection(1, 1, 1, 1)); - deleteAllLeftAction.run(null!, editor); + executeAction(deleteAllLeftAction, editor); assert.equal(model.getLinesContent()[0], 'onetwothree'); }); }); @@ -197,7 +210,7 @@ suite('Editor Contrib - Line Operations', () => { editor.setSelections([beforeSecondWasoSelection, endOfBCCSelection, endOfNonono]); - deleteAllLeftAction.run(null!, editor); + executeAction(deleteAllLeftAction, editor); let selections = editor.getSelections()!; assert.equal(model.getLineContent(2), ''); @@ -225,7 +238,7 @@ suite('Editor Contrib - Line Operations', () => { selections[2].endColumn ], [5, 1, 5, 1]); - deleteAllLeftAction.run(null!, editor); + executeAction(deleteAllLeftAction, editor); selections = editor.getSelections()!; assert.equal(model.getLineContent(1), 'hi my name is Carlos Matos waso waso'); @@ -263,24 +276,24 @@ suite('Editor Contrib - Line Operations', () => { let deleteAllLeftAction = new DeleteAllLeftAction(); editor.setSelections([new Selection(1, 2, 1, 2), new Selection(1, 4, 1, 4)]); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'lo', '001'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(1), 'lo'); editor.setSelections([new Selection(2, 2, 2, 2), new Selection(2, 4, 2, 5)]); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(2), 'd', '002'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(2), 'd'); editor.setSelections([new Selection(3, 2, 3, 5), new Selection(3, 7, 3, 7)]); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(3), 'world', '003'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(3), 'world'); editor.setSelections([new Selection(4, 3, 4, 3), new Selection(4, 5, 5, 4)]); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(4), 'jour', '004'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(4), 'jour'); editor.setSelections([new Selection(5, 3, 6, 3), new Selection(6, 5, 7, 5), new Selection(7, 7, 7, 7)]); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(5), 'world', '005'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(5), 'world'); }); }); @@ -300,7 +313,7 @@ suite('Editor Contrib - Line Operations', () => { assert.equal(model.getLineContent(1), 'Typing some text here on line one'); assert.deepEqual(editor.getSelection(), new Selection(1, 31, 1, 31)); - deleteAllLeftAction.run(null!, editor); + executeAction(deleteAllLeftAction, editor); assert.equal(model.getLineContent(1), 'one'); assert.deepEqual(editor.getSelection(), new Selection(1, 1, 1, 1)); @@ -331,29 +344,29 @@ suite('Editor Contrib - Line Operations', () => { let joinLinesAction = new JoinLinesAction(); editor.setSelection(new Selection(1, 2, 1, 2)); - joinLinesAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hello world', '001'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 6, 1, 6).toString(), '002'); + executeAction(joinLinesAction, editor); + assert.equal(model.getLineContent(1), 'hello world'); + assertSelection(editor, new Selection(1, 6, 1, 6)); editor.setSelection(new Selection(2, 2, 2, 2)); - joinLinesAction.run(null!, editor); - assert.equal(model.getLineContent(2), 'hello world', '003'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 7, 2, 7).toString(), '004'); + executeAction(joinLinesAction, editor); + assert.equal(model.getLineContent(2), 'hello world'); + assertSelection(editor, new Selection(2, 7, 2, 7)); editor.setSelection(new Selection(3, 2, 3, 2)); - joinLinesAction.run(null!, editor); - assert.equal(model.getLineContent(3), 'hello world', '005'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(3, 7, 3, 7).toString(), '006'); + executeAction(joinLinesAction, editor); + assert.equal(model.getLineContent(3), 'hello world'); + assertSelection(editor, new Selection(3, 7, 3, 7)); editor.setSelection(new Selection(4, 2, 5, 3)); - joinLinesAction.run(null!, editor); - assert.equal(model.getLineContent(4), 'hello world', '007'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(4, 2, 4, 8).toString(), '008'); + executeAction(joinLinesAction, editor); + assert.equal(model.getLineContent(4), 'hello world'); + assertSelection(editor, new Selection(4, 2, 4, 8)); editor.setSelection(new Selection(5, 1, 7, 3)); - joinLinesAction.run(null!, editor); - assert.equal(model.getLineContent(5), 'hello world', '009'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(5, 1, 5, 3).toString(), '010'); + executeAction(joinLinesAction, editor); + assert.equal(model.getLineContent(5), 'hello world'); + assertSelection(editor, new Selection(5, 1, 5, 3)); }); }); @@ -367,10 +380,10 @@ suite('Editor Contrib - Line Operations', () => { let joinLinesAction = new JoinLinesAction(); editor.setSelection(new Selection(2, 1, 2, 1)); - joinLinesAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hello', '001'); - assert.equal(model.getLineContent(2), 'world', '002'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 6, 2, 6).toString(), '003'); + executeAction(joinLinesAction, editor); + assert.equal(model.getLineContent(1), 'hello'); + assert.equal(model.getLineContent(2), 'world'); + assertSelection(editor, new Selection(2, 6, 2, 6)); }); }); @@ -402,19 +415,16 @@ suite('Editor Contrib - Line Operations', () => { new Selection(10, 1, 10, 1) ]); - joinLinesAction.run(null!, editor); - assert.equal(model.getLinesContent().join('\n'), 'hello world\nhello world\nhello world\nhello world\n\nhello world', '001'); - assert.deepEqual(editor.getSelections()!.toString(), [ + executeAction(joinLinesAction, editor); + assert.equal(model.getLinesContent().join('\n'), 'hello world\nhello world\nhello world\nhello world\n\nhello world'); + assertSelection(editor, [ /** primary cursor */ new Selection(3, 4, 3, 8), new Selection(1, 6, 1, 6), new Selection(2, 2, 2, 8), new Selection(4, 5, 4, 9), new Selection(6, 1, 6, 1) - ].toString(), '002'); - - /** primary cursor */ - assert.deepEqual(editor.getSelection()!.toString(), new Selection(3, 4, 3, 8).toString(), '003'); + ]); }); }); @@ -433,7 +443,7 @@ suite('Editor Contrib - Line Operations', () => { assert.equal(model.getLineContent(1), 'hello my dear'); assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 14)); - joinLinesAction.run(null!, editor); + executeAction(joinLinesAction, editor); assert.equal(model.getLineContent(1), 'hello my dear world'); assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 14)); @@ -456,29 +466,29 @@ suite('Editor Contrib - Line Operations', () => { let transposeAction = new TransposeAction(); editor.setSelection(new Selection(1, 1, 1, 1)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hello world', '001'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 2, 1, 2).toString(), '002'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(1), 'hello world'); + assertSelection(editor, new Selection(1, 2, 1, 2)); editor.setSelection(new Selection(1, 6, 1, 6)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hell oworld', '003'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 7, 1, 7).toString(), '004'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(1), 'hell oworld'); + assertSelection(editor, new Selection(1, 7, 1, 7)); editor.setSelection(new Selection(1, 12, 1, 12)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hell oworl', '005'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 2, 2, 2).toString(), '006'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(1), 'hell oworl'); + assertSelection(editor, new Selection(2, 2, 2, 2)); editor.setSelection(new Selection(3, 1, 3, 1)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(3), '', '007'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(4, 1, 4, 1).toString(), '008'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(3), ''); + assertSelection(editor, new Selection(4, 1, 4, 1)); editor.setSelection(new Selection(4, 2, 4, 2)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(4), ' ', '009'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(4, 3, 4, 3).toString(), '010'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(4), ' '); + assertSelection(editor, new Selection(4, 3, 4, 3)); } ); @@ -498,24 +508,24 @@ suite('Editor Contrib - Line Operations', () => { let transposeAction = new TransposeAction(); editor.setSelection(new Selection(1, 1, 1, 1)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(2), '', '011'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 1).toString(), '012'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(2), ''); + assertSelection(editor, new Selection(2, 1, 2, 1)); editor.setSelection(new Selection(3, 6, 3, 6)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(4), 'oworld', '013'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(4, 2, 4, 2).toString(), '014'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(4), 'oworld'); + assertSelection(editor, new Selection(4, 2, 4, 2)); editor.setSelection(new Selection(6, 12, 6, 12)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(7), 'd', '015'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(7, 2, 7, 2).toString(), '016'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(7), 'd'); + assertSelection(editor, new Selection(7, 2, 7, 2)); editor.setSelection(new Selection(8, 12, 8, 12)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(8), 'hello world', '019'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(8, 12, 8, 12).toString(), '020'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(8), 'hello world'); + assertSelection(editor, new Selection(8, 12, 8, 12)); } ); }); @@ -532,44 +542,44 @@ suite('Editor Contrib - Line Operations', () => { let titlecaseAction = new TitleCaseAction(); editor.setSelection(new Selection(1, 1, 1, 12)); - uppercaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'HELLO WORLD', '001'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 1, 12).toString(), '002'); + executeAction(uppercaseAction, editor); + assert.equal(model.getLineContent(1), 'HELLO WORLD'); + assertSelection(editor, new Selection(1, 1, 1, 12)); editor.setSelection(new Selection(1, 1, 1, 12)); - lowercaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hello world', '003'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 1, 12).toString(), '004'); + executeAction(lowercaseAction, editor); + assert.equal(model.getLineContent(1), 'hello world'); + assertSelection(editor, new Selection(1, 1, 1, 12)); editor.setSelection(new Selection(1, 3, 1, 3)); - uppercaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'HELLO world', '005'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 3, 1, 3).toString(), '006'); + executeAction(uppercaseAction, editor); + assert.equal(model.getLineContent(1), 'HELLO world'); + assertSelection(editor, new Selection(1, 3, 1, 3)); editor.setSelection(new Selection(1, 4, 1, 4)); - lowercaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hello world', '007'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 4, 1, 4).toString(), '008'); + executeAction(lowercaseAction, editor); + assert.equal(model.getLineContent(1), 'hello world'); + assertSelection(editor, new Selection(1, 4, 1, 4)); editor.setSelection(new Selection(1, 1, 1, 12)); - titlecaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'Hello World', '009'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 1, 12).toString(), '010'); + executeAction(titlecaseAction, editor); + assert.equal(model.getLineContent(1), 'Hello World'); + assertSelection(editor, new Selection(1, 1, 1, 12)); editor.setSelection(new Selection(2, 1, 2, 6)); - uppercaseAction.run(null!, editor); - assert.equal(model.getLineContent(2), 'ÖÇŞĞÜ', '011'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 6).toString(), '012'); + executeAction(uppercaseAction, editor); + assert.equal(model.getLineContent(2), 'ÖÇŞĞÜ'); + assertSelection(editor, new Selection(2, 1, 2, 6)); editor.setSelection(new Selection(2, 1, 2, 6)); - lowercaseAction.run(null!, editor); - assert.equal(model.getLineContent(2), 'öçşğü', '013'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 6).toString(), '014'); + executeAction(lowercaseAction, editor); + assert.equal(model.getLineContent(2), 'öçşğü'); + assertSelection(editor, new Selection(2, 1, 2, 6)); editor.setSelection(new Selection(2, 1, 2, 6)); - titlecaseAction.run(null!, editor); - assert.equal(model.getLineContent(2), 'Öçşğü', '015'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 6).toString(), '016'); + executeAction(titlecaseAction, editor); + assert.equal(model.getLineContent(2), 'Öçşğü'); + assertSelection(editor, new Selection(2, 1, 2, 6)); } ); @@ -586,27 +596,27 @@ suite('Editor Contrib - Line Operations', () => { let titlecaseAction = new TitleCaseAction(); editor.setSelection(new Selection(1, 1, 1, 12)); - titlecaseAction.run(null!, editor); + executeAction(titlecaseAction, editor); assert.equal(model.getLineContent(1), 'Foo Bar Baz'); editor.setSelection(new Selection(2, 1, 2, 12)); - titlecaseAction.run(null!, editor); + executeAction(titlecaseAction, editor); assert.equal(model.getLineContent(2), 'Foo\'Bar\'Baz'); editor.setSelection(new Selection(3, 1, 3, 12)); - titlecaseAction.run(null!, editor); + executeAction(titlecaseAction, editor); assert.equal(model.getLineContent(3), 'Foo[Bar]Baz'); editor.setSelection(new Selection(4, 1, 4, 12)); - titlecaseAction.run(null!, editor); + executeAction(titlecaseAction, editor); assert.equal(model.getLineContent(4), 'Foo`Bar~Baz'); editor.setSelection(new Selection(5, 1, 5, 12)); - titlecaseAction.run(null!, editor); + executeAction(titlecaseAction, editor); assert.equal(model.getLineContent(5), 'Foo^Bar%Baz'); editor.setSelection(new Selection(6, 1, 6, 12)); - titlecaseAction.run(null!, editor); + executeAction(titlecaseAction, editor); assert.equal(model.getLineContent(6), 'Foo$Bar!Baz'); } ); @@ -621,24 +631,24 @@ suite('Editor Contrib - Line Operations', () => { let lowercaseAction = new LowerCaseAction(); editor.setSelection(new Selection(1, 1, 1, 1)); - uppercaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), '', '013'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 1, 1).toString(), '014'); + executeAction(uppercaseAction, editor); + assert.equal(model.getLineContent(1), ''); + assertSelection(editor, new Selection(1, 1, 1, 1)); editor.setSelection(new Selection(1, 1, 1, 1)); - lowercaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), '', '015'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 1, 1).toString(), '016'); + executeAction(lowercaseAction, editor); + assert.equal(model.getLineContent(1), ''); + assertSelection(editor, new Selection(1, 1, 1, 1)); editor.setSelection(new Selection(2, 2, 2, 2)); - uppercaseAction.run(null!, editor); - assert.equal(model.getLineContent(2), ' ', '017'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 2, 2, 2).toString(), '018'); + executeAction(uppercaseAction, editor); + assert.equal(model.getLineContent(2), ' '); + assertSelection(editor, new Selection(2, 2, 2, 2)); editor.setSelection(new Selection(2, 2, 2, 2)); - lowercaseAction.run(null!, editor); - assert.equal(model.getLineContent(2), ' ', '019'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 2, 2, 2).toString(), '020'); + executeAction(lowercaseAction, editor); + assert.equal(model.getLineContent(2), ' '); + assertSelection(editor, new Selection(2, 2, 2, 2)); } ); }); @@ -649,17 +659,17 @@ suite('Editor Contrib - Line Operations', () => { const model = editor.getModel()!; const action = new DeleteAllRightAction(); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['']); assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); editor.setSelection(new Selection(1, 1, 1, 1)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['']); assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); editor.setSelections([new Selection(1, 1, 1, 1), new Selection(1, 1, 1, 1), new Selection(1, 1, 1, 1)]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['']); assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); }); @@ -674,17 +684,17 @@ suite('Editor Contrib - Line Operations', () => { const action = new DeleteAllRightAction(); editor.setSelection(new Selection(1, 2, 1, 5)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['ho', 'world']); assert.deepEqual(editor.getSelections(), [new Selection(1, 2, 1, 2)]); editor.setSelection(new Selection(1, 1, 2, 4)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['ld']); assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); editor.setSelection(new Selection(1, 1, 1, 3)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['']); assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); }); @@ -699,12 +709,12 @@ suite('Editor Contrib - Line Operations', () => { const action = new DeleteAllRightAction(); editor.setSelection(new Selection(1, 3, 1, 3)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['he', 'world']); assert.deepEqual(editor.getSelections(), [new Selection(1, 3, 1, 3)]); editor.setSelection(new Selection(2, 1, 2, 1)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['he', '']); assert.deepEqual(editor.getSelections(), [new Selection(2, 1, 2, 1)]); }); @@ -719,17 +729,17 @@ suite('Editor Contrib - Line Operations', () => { const action = new DeleteAllRightAction(); editor.setSelection(new Selection(1, 6, 1, 6)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['helloworld']); assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]); editor.setSelection(new Selection(1, 6, 1, 6)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['hello']); assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]); editor.setSelection(new Selection(1, 6, 1, 6)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['hello']); assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]); }); @@ -749,34 +759,34 @@ suite('Editor Contrib - Line Operations', () => { new Selection(1, 6, 1, 6), new Selection(3, 4, 3, 4), ]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['hethere', 'wor']); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3), new Selection(2, 4, 2, 4) ]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['he', 'wor']); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3), new Selection(2, 4, 2, 4) ]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['hewor']); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3), new Selection(1, 6, 1, 6) ]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['he']); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3) ]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['he']); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3) @@ -798,7 +808,7 @@ suite('Editor Contrib - Line Operations', () => { new Selection(1, 6, 1, 6), new Selection(3, 4, 3, 4), ]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['hethere', 'wor']); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3), @@ -831,7 +841,7 @@ suite('Editor Contrib - Line Operations', () => { editor.setPosition(new Position(lineNumber, column)); let insertLineBeforeAction = new InsertLineBeforeAction(); - insertLineBeforeAction.run(null!, editor); + executeAction(insertLineBeforeAction, editor); callback(editor.getModel()!, cursor); }); } @@ -872,7 +882,7 @@ suite('Editor Contrib - Line Operations', () => { editor.setPosition(new Position(lineNumber, column)); let insertLineAfterAction = new InsertLineAfterAction(); - insertLineAfterAction.run(null!, editor); + executeAction(insertLineAfterAction, editor); callback(editor.getModel()!, cursor); }); } @@ -917,7 +927,7 @@ suite('Editor Contrib - Line Operations', () => { let indentLinesAction = new IndentLinesAction(); editor.setPosition(new Position(1, 2)); - indentLinesAction.run(null!, editor); + executeAction(indentLinesAction, editor); assert.equal(model.getLineContent(1), '\tfunction baz() {'); assert.deepEqual(editor.getSelection(), new Selection(1, 3, 1, 3)); @@ -942,7 +952,7 @@ suite('Editor Contrib - Line Operations', () => { const indentLinesAction = new IndentLinesAction(); editor.setPosition(new Position(1, 1)); - indentLinesAction.run(null!, editor); + executeAction(indentLinesAction, editor); assert.equal(model.getLineContent(1), '\tSome text'); assert.deepEqual(editor.getSelection(), new Selection(1, 2, 1, 2)); }); @@ -964,7 +974,7 @@ suite('Editor Contrib - Line Operations', () => { new Selection(3, 4, 3, 4), ]); const deleteLinesAction = new DeleteLinesAction(); - deleteLinesAction.run(null!, editor); + executeAction(deleteLinesAction, editor); assert.equal(editor.getValue(), 'a\nc'); }); @@ -976,7 +986,7 @@ suite('Editor Contrib - Line Operations', () => { withTestCodeEditor(initialText, {}, (editor) => { editor.setSelections(initialSelections); const deleteLinesAction = new DeleteLinesAction(); - deleteLinesAction.run(null!, editor); + executeAction(deleteLinesAction, editor); assert.equal(editor.getValue(), resultingText.join('\n')); assert.deepEqual(editor.getSelections(), resultingSelections); diff --git a/src/vs/editor/test/browser/commands/shiftCommand.test.ts b/src/vs/editor/test/browser/commands/shiftCommand.test.ts index da26bb2030c..0a6ada7efbf 100644 --- a/src/vs/editor/test/browser/commands/shiftCommand.test.ts +++ b/src/vs/editor/test/browser/commands/shiftCommand.test.ts @@ -22,7 +22,8 @@ import { EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions' export function createSingleEditOp(text: string, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): IIdentifiedSingleEditOperation { return { range: new Range(selectionLineNumber, selectionColumn, positionLineNumber, positionColumn), - text: text + text: text, + forceMoveMarkers: false }; } diff --git a/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts b/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts index bf414fa1503..af0c64e537c 100644 --- a/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts +++ b/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts @@ -28,7 +28,8 @@ function createInsertDeleteSingleEditOp(text: string | null, positionLineNumber: export function createSingleEditOp(text: string | null, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): IIdentifiedSingleEditOperation { return { range: new Range(selectionLineNumber, selectionColumn, positionLineNumber, positionColumn), - text: text + text: text, + forceMoveMarkers: false }; } diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 7b9cafb8c95..0f300688e23 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -2279,6 +2279,30 @@ suite('Editor Controller - Regression tests', () => { model.dispose(); }); + + test('issue #85712: Paste line moves cursor to start of current line rather than start of next line', () => { + let model = createTextModel( + [ + 'abc123', + '' + ].join('\n') + ); + + withTestCodeEditor(null, { model: model }, (editor, cursor) => { + editor.setSelections([ + new Selection(2, 1, 2, 1) + ]); + cursorCommand(cursor, H.Paste, { text: 'something\n', pasteOnNewLine: true }); + assert.equal(model.getValue(), [ + 'abc123', + 'something', + '' + ].join('\n')); + assertCursor(cursor, new Position(3, 1)); + }); + + model.dispose(); + }); }); suite('Editor Controller - Cursor Configuration', () => { @@ -2636,7 +2660,7 @@ suite('Editor Controller - Cursor Configuration', () => { '', ' }', ].join('\n')); - assertCursor(cursor, new Position(4, 1)); + assertCursor(cursor, new Position(5, 1)); }); model.dispose(); diff --git a/src/vs/editor/test/browser/testCommand.ts b/src/vs/editor/test/browser/testCommand.ts index ce33b94a913..8126f24735a 100644 --- a/src/vs/editor/test/browser/testCommand.ts +++ b/src/vs/editor/test/browser/testCommand.ts @@ -50,17 +50,19 @@ export function testCommand( export function getEditOperation(model: ITextModel, command: ICommand): IIdentifiedSingleEditOperation[] { let operations: IIdentifiedSingleEditOperation[] = []; let editOperationBuilder: IEditOperationBuilder = { - addEditOperation: (range: Range, text: string) => { + addEditOperation: (range: Range, text: string, forceMoveMarkers: boolean = false) => { operations.push({ range: range, - text: text + text: text, + forceMoveMarkers: forceMoveMarkers }); }, - addTrackedEditOperation: (range: Range, text: string) => { + addTrackedEditOperation: (range: Range, text: string, forceMoveMarkers: boolean = false) => { operations.push({ range: range, - text: text + text: text, + forceMoveMarkers: forceMoveMarkers }); }, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 3a6307fbbda..2c32863e855 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1906,14 +1906,14 @@ declare namespace monaco.editor { * @param range The range to replace (delete). May be empty to represent a simple insert. * @param text The text to replace with. May be null to represent a simple delete. */ - addEditOperation(range: Range, text: string | null): void; + addEditOperation(range: Range, text: string | null, forceMoveMarkers?: boolean): void; /** * Add a new edit operation (a replace operation). * The inverse edits will be accessible in `ICursorStateComputerData.getInverseEditOperations()` * @param range The range to replace (delete). May be empty to represent a simple insert. * @param text The text to replace with. May be null to represent a simple delete. */ - addTrackedEditOperation(range: Range, text: string | null): void; + addTrackedEditOperation(range: Range, text: string | null, forceMoveMarkers?: boolean): void; /** * Track `selection` when applying edit operations. * A best effort will be made to not grow/expand the selection. From e6233feb84b184dbc05dad0fbc6dc97f2ac37d87 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 15 Jan 2020 11:17:09 +0100 Subject: [PATCH 313/843] typeLabel -> detailsLabel, restore old visibility model --- .../editor/contrib/suggest/media/suggest.css | 40 +++---------------- .../editor/contrib/suggest/suggestWidget.ts | 11 +++-- 2 files changed, 13 insertions(+), 38 deletions(-) diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index 5efde04b9ae..5e7b46ffba1 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -129,7 +129,7 @@ /** Type Info and icon next to the label in the focused completion item **/ -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .details-label { margin-left: 0.8em; flex: 1; text-align: right; @@ -139,48 +139,20 @@ white-space: nowrap; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label > .monaco-tokenized-source { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .details-label > .monaco-tokenized-source { display: inline; } -/** Inline type Label (details) **/ - -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label, -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .type-label { - display: none; -} - -.monaco-editor .suggest-widget.always-reveal-inline-details .monaco-list .monaco-list-row > .contents > .main > .type-label, -.monaco-editor .suggest-widget.always-reveal-inline-details.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .type-label { - display: inline; -} - -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .type-label { - display: inline; -} - -/** readMore icon **/ - .monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .details-label, .monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .details-label, .monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { display: none; } -.monaco-editor .suggest-widget.always-reveal-inline-details .monaco-list .monaco-list-row > .contents > .main > .readMore, -.monaco-editor .suggest-widget.always-reveal-inline-details.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, -.monaco-editor .suggest-widget.always-reveal-inline-details.docs-below .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { - display: inline; -} - -.monaco-editor .suggest-widget.always-reveal-inline-details .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { - visibility: visible; -} -.monaco-editor .suggest-widget.always-reveal-inline-details .monaco-list .monaco-list-row:not(.focused) > .contents > .main > .readMore { - visibility: hidden; -} - -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .details-label { display: inline; } diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 3ef5f9af35d..b0b1b7ee20b 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -51,7 +51,10 @@ interface ISuggestionTemplateData { colorspan: HTMLElement; iconLabel: IconLabel; iconContainer: HTMLElement; - typeLabel: HTMLElement; + /** + * Showing either `CompletionItem#details` or `CompletionItemLabel#name` + */ + detailsLabel: HTMLElement; readMore: HTMLElement; disposables: DisposableStore; } @@ -128,7 +131,7 @@ class Renderer implements IListRenderer data.iconLabel = new IconLabel(main, { supportHighlights: true, supportCodicons: true }); data.disposables.add(data.iconLabel); - data.typeLabel = append(main, $('span.type-label')); + data.detailsLabel = append(main, $('span.details-label')); data.readMore = append(main, $('span.readMore.codicon.codicon-info')); data.readMore.title = nls.localize('readMore', "Read More...{0}", this.triggerKeybindingLabel); @@ -214,9 +217,9 @@ class Renderer implements IListRenderer data.iconLabel.setLabel(textLabel, undefined, labelOptions); if (typeof suggestion.label === 'string') { - data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); + data.detailsLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); } else { - data.typeLabel.textContent = (suggestion.label.details || '').replace(/\n.*$/m, ''); + data.detailsLabel.textContent = (suggestion.label.details || '').replace(/\n.*$/m, ''); } if (canExpandCompletionItem(element)) { From e1710168ea25fddbf60233cce5cfccd3d67d8927 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 11:17:22 +0100 Subject: [PATCH 314/843] Fix #88646 --- .../platform/userDataSync/common/keybindingsSync.ts | 12 +++++++----- src/vs/platform/userDataSync/common/settingsSync.ts | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index cac2ea5826c..944de280a6b 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -177,9 +177,13 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser this.setStatus(SyncStatus.HasConflicts); return false; } - await this.apply(); - this.logService.trace('Keybindings: Finished synchronizing keybindings...'); - return true; + try { + await this.apply(); + this.logService.trace('Keybindings: Finished synchronizing keybindings...'); + return true; + } finally { + this.setStatus(SyncStatus.Idle); + } } catch (e) { this.syncPreviewResultPromise = null; this.setStatus(SyncStatus.Idle); @@ -194,8 +198,6 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser return this.sync(); } throw e; - } finally { - this.setStatus(SyncStatus.Idle); } } diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index f0723de37a6..d2ba863515a 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -234,9 +234,13 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer this.setStatus(SyncStatus.HasConflicts); return false; } - await this.apply(); - this.logService.trace('Settings: Finished synchronizing settings.'); - return true; + try { + await this.apply(); + this.logService.trace('Settings: Finished synchronizing settings.'); + return true; + } finally { + this.setStatus(SyncStatus.Idle); + } } catch (e) { this.syncPreviewResultPromise = null; this.setStatus(SyncStatus.Idle); @@ -251,8 +255,6 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer return this.sync(); } throw e; - } finally { - this.setStatus(SyncStatus.Idle); } } From c5fb0adc0e2d83b1b3c4c7f17c4c822247f0ea9a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 15 Jan 2020 11:18:06 +0100 Subject: [PATCH 315/843] fixes #86033 --- src/vs/base/browser/ui/grid/gridview.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 88716fd28d3..27222583608 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -392,6 +392,8 @@ class BranchNode implements ISplitView, IDisposable { const child = this._removeChild(from); this._addChild(child, to); + + this.onDidChildrenChange(); } swapChildren(from: number, to: number): void { @@ -408,6 +410,8 @@ class BranchNode implements ISplitView, IDisposable { this.splitview.swapViews(from, to); [this.children[from].orthogonalStartSash, this.children[from].orthogonalEndSash, this.children[to].orthogonalStartSash, this.children[to].orthogonalEndSash] = [this.children[to].orthogonalStartSash, this.children[to].orthogonalEndSash, this.children[from].orthogonalStartSash, this.children[from].orthogonalEndSash]; [this.children[from], this.children[to]] = [this.children[to], this.children[from]]; + + this.onDidChildrenChange(); } resizeChild(index: number, size: number): void { From ea79f0a9b9d9cc878a9426f733c7f9be682a57fd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jan 2020 11:20:37 +0100 Subject: [PATCH 316/843] untitled - reduce service methods --- .../electron-browser/backupOnShutdown.test.ts | 3 -- .../electron-browser/backupRestorer.test.ts | 3 +- .../electron-browser/backupTracker.test.ts | 1 - .../textfile/browser/textFileService.ts | 34 +++++++++--- .../textfile/test/textFileService.io.test.ts | 5 +- .../textfile/test/textFileService.test.ts | 3 +- .../test/textModelResolverService.test.ts | 2 +- .../common/untitledTextEditorService.ts | 54 ------------------- .../test/common/editor/editor.test.ts | 4 +- .../common/editor/untitledTextEditor.test.ts | 24 ++++++--- 10 files changed, 52 insertions(+), 81 deletions(-) diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupOnShutdown.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupOnShutdown.test.ts index e8d36fc4af6..c9ce22aebb8 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupOnShutdown.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupOnShutdown.test.ts @@ -11,7 +11,6 @@ import { toResource } from 'vs/base/test/common/utils'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { HotExitConfiguration, IFileService } from 'vs/platform/files/common/files'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; @@ -29,7 +28,6 @@ class ServiceAccessor { @ILifecycleService public lifecycleService: TestLifecycleService, @ITextFileService public textFileService: TestTextFileService, @IFilesConfigurationService public filesConfigurationService: TestFilesConfigurationService, - @IUntitledTextEditorService public untitledTextEditorService: IUntitledTextEditorService, @IWorkspaceContextService public contextService: TestContextService, @IModelService public modelService: ModelServiceImpl, @IFileService public fileService: TestFileService, @@ -69,7 +67,6 @@ suite('BackupOnShutdown', () => { model.dispose(); } (accessor.textFileService.models).dispose(); - accessor.untitledTextEditorService.revertAll(); backupOnShutdown.dispose(); }); diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts index ed9707995e9..832375b29dc 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts @@ -87,7 +87,6 @@ suite('BackupRestorer', () => { disposables = []; (accessor.textFileService.models).dispose(); - accessor.untitledTextEditorService.revertAll(); return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); }); @@ -132,10 +131,12 @@ suite('BackupRestorer', () => { if (isEqual(resource, untitledFile1)) { const model = await accessor.untitledTextEditorService.createOrGet(resource).resolve(); assert.equal(model.textEditorModel.getValue(), 'untitled-1'); + model.dispose(); counter++; } else if (isEqual(resource, untitledFile2)) { const model = await accessor.untitledTextEditorService.createOrGet(resource).resolve(); assert.equal(model.textEditorModel.getValue(), 'untitled-2'); + model.dispose(); counter++; } else if (isEqual(resource, fooFile)) { const model = await accessor.textFileService.models.get(resource!)?.load(); diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts index 533280f5c3b..7c61a061a8c 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts @@ -93,7 +93,6 @@ suite('BackupTracker', () => { disposables = []; (accessor.textFileService.models).dispose(); - accessor.untitledTextEditorService.revertAll(); return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); }); diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index d5bbe8bc182..9e2025d556e 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -592,7 +592,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex } private suggestFileName(untitledResource: URI): URI { - const untitledFileName = this.untitledTextEditorService.suggestFileName(untitledResource); + const untitledFileName = this.untitledTextEditorService.exists(untitledResource) ? this.untitledTextEditorService.createOrGet(untitledResource).suggestFileName() : basename(untitledResource); const remoteAuthority = this.environmentService.configuration.remoteAuthority; const schemeFilter = remoteAuthority ? Schemas.vscodeRemote : Schemas.file; @@ -618,19 +618,23 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return !(await this.doRevertAll([resource], options)).results.some(result => result.error); } - private async doRevertAll(resources?: URI[], options?: IRevertOptions): Promise { + private async doRevertAll(resources: URI[], options?: IRevertOptions): Promise { // Revert files first - const revertOperationResult = await this.doRevertAllFiles(resources, options); + const revertFileOperationResult = await this.doRevertAllFiles(resources, options); // Revert untitled - const untitledReverted = this.untitledTextEditorService.revertAll(resources); - untitledReverted.forEach(untitled => revertOperationResult.results.push({ source: untitled })); + const revertUntitledOperationResult = await this.doRevertAllUntitled(resources, options); - return revertOperationResult; + return { + results: [ + ...revertFileOperationResult.results, + ...revertUntitledOperationResult.results + ] + }; } - private async doRevertAllFiles(resources?: URI[], options?: IRevertOptions): Promise { + private async doRevertAllFiles(resources: URI[], options?: IRevertOptions): Promise { const fileModels = options?.force ? this.getFileModels(resources) : this.getDirtyFileModels(resources); const mapResourceToResult = new ResourceMap(); @@ -668,6 +672,22 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return { results: mapResourceToResult.values() }; } + private async doRevertAllUntitled(resources: URI[], options?: IRevertOptions): Promise { + const result: ITextFileOperationResult = { results: [] }; + + await Promise.all(resources.map(resource => { + if (this.untitledTextEditorService.exists(resource)) { + result.results.push({ source: resource }); + + return this.untitledTextEditorService.createOrGet(resource).revert(options); + } + + return Promise.resolve(undefined); + })); + + return result; + } + //#endregion //#region dirty diff --git a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts index 9b882d978e3..a09cc6b3f8c 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts @@ -6,7 +6,6 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/workbenchTestServices'; import { ITextFileService, snapshotToString, TextFileOperationResult, TextFileOperationError } from 'vs/workbench/services/textfile/common/textfiles'; -import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { IFileService } from 'vs/platform/files/common/files'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; import { Schemas } from 'vs/base/common/network'; @@ -31,8 +30,7 @@ import { detectEncodingByBOM } from 'vs/base/test/node/encoding/encoding.test'; class ServiceAccessor { constructor( - @ITextFileService public textFileService: TestTextFileService, - @IUntitledTextEditorService public untitledTextEditorService: IUntitledTextEditorService + @ITextFileService public textFileService: TestTextFileService ) { } } @@ -95,7 +93,6 @@ suite('Files - TextFileService i/o', () => { teardown(async () => { (accessor.textFileService.models).dispose(); - accessor.untitledTextEditorService.revertAll(); disposables.clear(); diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts index e3d5a9b8e0a..285e0f9e776 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -53,7 +53,6 @@ suite('Files - TextFileService', () => { model.dispose(); } (accessor.textFileService.models).dispose(); - accessor.untitledTextEditorService.revertAll(); }); test('isDirty/getDirty - files and untitled', async function () { @@ -79,6 +78,8 @@ suite('Files - TextFileService', () => { assert.ok(accessor.textFileService.isDirty(untitled.getResource())); assert.equal(accessor.textFileService.getDirty().length, 2); assert.equal(accessor.textFileService.getDirty([untitled.getResource()])[0].toString(), untitled.getResource().toString()); + + untitledModel.dispose(); }); test('save - file', async function () { diff --git a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts index 516c26c69cf..4a1f56df05c 100644 --- a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts +++ b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts @@ -49,7 +49,6 @@ suite('Workbench - TextModelResolverService', () => { model = (undefined)!; } (accessor.textFileService.models).dispose(); - accessor.untitledTextEditorService.revertAll(); }); test('resolve resource', async () => { @@ -120,6 +119,7 @@ suite('Workbench - TextModelResolverService', () => { assert.ok(editorModel); ref.dispose(); input.dispose(); + model.dispose(); }); test('even loading documents should be refcounted', async () => { diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts index 42c6848278a..58f1ad6a982 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts @@ -14,7 +14,6 @@ import { ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { Disposable } from 'vs/base/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { basename } from 'vs/base/common/resources'; export const IUntitledTextEditorService = createDecorator('untitledTextEditorService'); @@ -60,16 +59,6 @@ export interface IUntitledTextEditorService { */ isDirty(resource: URI): boolean; - /** - * Find out if a backup with the provided resource exists and has a backup on disk. - */ - hasBackup(resource: URI): boolean; - - /** - * Reverts the untitled resources if found. - */ - revertAll(resources?: URI[]): URI[]; - /** * Creates a new untitled input with the optional resource URI or returns an existing one * if the provided resource exists already as untitled input. @@ -84,16 +73,6 @@ export interface IUntitledTextEditorService { * A check to find out if a untitled resource has a file path associated or not. */ hasAssociatedFilePath(resource: URI): boolean; - - /** - * Suggests a filename for the given untitled resource if it is known. - */ - suggestFileName(resource: URI): string; - - /** - * Get the configured encoding for the given untitled resource if any. - */ - getEncoding(resource: URI): string | undefined; } export class UntitledTextEditorService extends Disposable implements IUntitledTextEditorService { @@ -136,33 +115,12 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe return this.mapResourceToInput.has(resource); } - revertAll(resources?: URI[], force?: boolean): URI[] { - const reverted: URI[] = []; - - const untitledInputs = this.getAll(resources); - untitledInputs.forEach(input => { - if (input) { - input.revert(); - - reverted.push(input.getResource()); - } - }); - - return reverted; - } - isDirty(resource: URI): boolean { const input = this.get(resource); return input ? input.isDirty() : false; } - hasBackup(resource: URI): boolean { - const input = this.get(resource); - - return input ? input.hasBackup() : false; - } - getDirty(resources?: URI[]): URI[] { let inputs: UntitledTextEditorInput[]; if (resources) { @@ -253,18 +211,6 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe hasAssociatedFilePath(resource: URI): boolean { return this.mapResourceToAssociatedFilePath.has(resource); } - - suggestFileName(resource: URI): string { - const input = this.get(resource); - - return input ? input.suggestFileName() : basename(resource); - } - - getEncoding(resource: URI): string | undefined { - const input = this.get(resource); - - return input ? input.getEncoding() : undefined; - } } registerSingleton(IUntitledTextEditorService, UntitledTextEditorService, true); diff --git a/src/vs/workbench/test/common/editor/editor.test.ts b/src/vs/workbench/test/common/editor/editor.test.ts index d89ecd8db5b..e5114d20e2d 100644 --- a/src/vs/workbench/test/common/editor/editor.test.ts +++ b/src/vs/workbench/test/common/editor/editor.test.ts @@ -14,8 +14,7 @@ import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestSe import { Schemas } from 'vs/base/common/network'; class ServiceAccessor { - constructor(@IUntitledTextEditorService public untitledTextEditorService: UntitledTextEditorService) { - } + constructor(@IUntitledTextEditorService public untitledTextEditorService: UntitledTextEditorService) { } } class FileEditorInput extends EditorInput { @@ -48,7 +47,6 @@ suite('Workbench editor', () => { }); teardown(() => { - accessor.untitledTextEditorService.revertAll(); accessor.untitledTextEditorService.dispose(); }); diff --git a/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts b/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts index ca9bba1e27b..18da33dd4b9 100644 --- a/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts +++ b/src/vs/workbench/test/common/editor/untitledTextEditor.test.ts @@ -44,7 +44,6 @@ suite('Workbench untitled text editors', () => { }); teardown(() => { - accessor.untitledTextEditorService.revertAll(); accessor.untitledTextEditorService.dispose(); }); @@ -67,8 +66,8 @@ suite('Workbench untitled text editors', () => { assert.equal(service.getAll().length, 2); assert.equal(service.getAll([input1.getResource(), input2.getResource()]).length, 2); - // revertAll() - service.revertAll([input1.getResource()]); + // revert() + input1.revert(); assert.ok(input1.isDisposed()); assert.equal(service.getAll().length, 1); @@ -90,7 +89,8 @@ suite('Workbench untitled text editors', () => { assert.ok(workingCopyService.isDirty(input2.getResource())); assert.equal(workingCopyService.dirtyCount, 1); - service.revertAll(); + input1.revert(); + input2.revert(); assert.equal(service.getAll().length, 0); assert.ok(!input2.isDirty()); assert.ok(!model.isDirty()); @@ -109,6 +109,9 @@ suite('Workbench untitled text editors', () => { }); model.textEditorModel.setValue('foo bar'); + model.dispose(); + input1.dispose(); + input2.dispose(); }); test('Untitled with associated resource is dirty', () => { @@ -136,6 +139,7 @@ suite('Workbench untitled text editors', () => { assert.ok(!model.isDirty()); assert.ok(!workingCopyService.isDirty(model.resource)); input.dispose(); + model.dispose(); }); test('Untitled via createOrGet options', async () => { @@ -174,7 +178,8 @@ suite('Workbench untitled text editors', () => { const service = accessor.untitledTextEditorService; const input = service.createOrGet(); - assert.ok(service.suggestFileName(input.getResource())); + assert.ok(input.suggestFileName().length > 0); + input.dispose(); }); test('Untitled with associated path remains dirty when content gets empty', async () => { @@ -189,6 +194,7 @@ suite('Workbench untitled text editors', () => { model.textEditorModel.setValue(''); assert.ok(model.isDirty()); input.dispose(); + model.dispose(); }); test('Untitled with initial content is dirty', async () => { @@ -211,6 +217,7 @@ suite('Workbench untitled text editors', () => { untitled.dispose(); listener.dispose(); + model.dispose(); }); test('Untitled created with files.defaultLanguage setting', () => { @@ -281,6 +288,7 @@ suite('Workbench untitled text editors', () => { assert.equal(input.getMode(), PLAINTEXT_MODE_ID); input.dispose(); + model.dispose(); }); test('encoding change event', async () => { @@ -299,6 +307,7 @@ suite('Workbench untitled text editors', () => { model.setEncoding('utf16'); assert.equal(counter, 1); input.dispose(); + model.dispose(); }); test('onDidChangeContent event', async function () { @@ -324,6 +333,7 @@ suite('Workbench untitled text editors', () => { assert.equal(counter, 4, 'Dirty model should trigger event'); input.dispose(); + model.dispose(); }); test('onDidChangeDirty event', async function () { @@ -343,6 +353,7 @@ suite('Workbench untitled text editors', () => { assert.equal(counter, 1, 'Another change does not fire event'); input.dispose(); + model.dispose(); }); test('onDidDisposeModel event', async () => { @@ -356,9 +367,10 @@ suite('Workbench untitled text editors', () => { assert.equal(r.toString(), input.getResource().toString()); }); - await input.resolve(); + const model = await input.resolve(); assert.equal(counter, 0); input.dispose(); assert.equal(counter, 1); + model.dispose(); }); }); From 6278e4ff51b82b96e769e7552faf23c8d856918c Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 15 Jan 2020 11:25:32 +0100 Subject: [PATCH 317/843] use WorkbenchCompressibleAsyncDataTree --- .../debug/browser/loadedScriptsView.ts | 63 ++++++++++++++----- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index 0866f659ed8..99785153668 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -26,17 +26,19 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { ResourceLabels, IResourceLabelProps, IResourceLabelOptions, IResourceLabel } from 'vs/workbench/browser/labels'; import { FileKind } from 'vs/platform/files/common/files'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; +import { ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; +import { TreeResourceNavigator2, WorkbenchCompressibleAsyncDataTree } from 'vs/platform/list/browser/listService'; import { dispose } from 'vs/base/common/lifecycle'; import { createMatches, FuzzyScore } from 'vs/base/common/filters'; import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; import { ILabelService } from 'vs/platform/label/common/label'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import type { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; +import type { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; -const SMART = true; +const NEW_STYLE_COMPRESS = true; // RFC 2396, Appendix A: https://www.ietf.org/rfc/rfc2396.txt const URI_SCHEMA_PATTERN = /^[a-zA-Z][a-zA-Z0-9\+\-\.]+:/; @@ -49,7 +51,7 @@ class BaseTreeItem { private _children = new Map(); private _source: Source | undefined; - constructor(private _parent: BaseTreeItem | undefined, private _label: string) { + constructor(private _parent: BaseTreeItem | undefined, private _label: string, public readonly isIncompressible = false) { this._showedMoreThanOne = false; } @@ -205,7 +207,7 @@ class BaseTreeItem { } private oneChild(): BaseTreeItem | undefined { - if (SMART && !this._source && !this._showedMoreThanOne && !(this instanceof RootFolderTreeItem) && !(this instanceof SessionTreeItem)) { + if (!this._source && !this._showedMoreThanOne && this.skipOneChild()) { if (this._children.size === 1) { return this._children.values().next().value; } @@ -216,19 +218,28 @@ class BaseTreeItem { } return undefined; } + + private skipOneChild(): boolean { + if (NEW_STYLE_COMPRESS) { + // if the root node has only one Session, don't show the session + return this instanceof RootTreeItem; + } else { + return !(this instanceof RootFolderTreeItem) && !(this instanceof SessionTreeItem); + } + } } class RootFolderTreeItem extends BaseTreeItem { constructor(parent: BaseTreeItem, public folder: IWorkspaceFolder) { - super(parent, folder.name); + super(parent, folder.name, true); } } class RootTreeItem extends BaseTreeItem { constructor(private _debugModel: IDebugModel, private _environmentService: IEnvironmentService, private _contextService: IWorkspaceContextService, private _labelService: ILabelService) { - super(undefined, 'Root'); + super(undefined, 'Root', true); this._debugModel.getSessions().forEach(session => { this.add(session); }); @@ -253,7 +264,7 @@ class SessionTreeItem extends BaseTreeItem { private _labelService: ILabelService; constructor(labelService: ILabelService, parent: BaseTreeItem, session: IDebugSession, private _environmentService: IEnvironmentService, private rootProvider: IWorkspaceContextService) { - super(parent, session.getLabel()); + super(parent, session.getLabel(), true); this._labelService = labelService; this._initialized = false; this._session = session; @@ -392,7 +403,7 @@ export class LoadedScriptsView extends ViewPane { private treeContainer!: HTMLElement; private loadedScriptsItemType: IContextKey; - private tree!: WorkbenchAsyncDataTree; + private tree!: WorkbenchCompressibleAsyncDataTree; private treeLabels!: ResourceLabels; private changeScheduler!: RunOnceScheduler; private treeNeedsRefreshOnVisible = false; @@ -428,15 +439,25 @@ export class LoadedScriptsView extends ViewPane { this.treeLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility }); this._register(this.treeLabels); - this.tree = this.instantiationService.createInstance>(WorkbenchAsyncDataTree, 'LoadedScriptsView', this.treeContainer, new LoadedScriptsDelegate(), + this.tree = this.instantiationService.createInstance>( + WorkbenchCompressibleAsyncDataTree, + 'LoadedScriptsView', + this.treeContainer, + new LoadedScriptsDelegate(), + { + isIncompressible: (element: BaseTreeItem): boolean => element.isIncompressible + }, [new LoadedScriptsRenderer(this.treeLabels)], new LoadedScriptsDataSource(), { + compressionEnabled: NEW_STYLE_COMPRESS, + autoExpandSingleChildren: NEW_STYLE_COMPRESS, identityProvider: { getId: (element: LoadedScriptsItem) => element.getId() }, keyboardNavigationLabelProvider: { - getKeyboardNavigationLabel: (element: LoadedScriptsItem) => element.getLabel() + getKeyboardNavigationLabel: (element: LoadedScriptsItem) => element.getLabel(), + getCompressedNodeKeyboardNavigationLabel: () => 'foo' // TODO }, filter: this.filter, accessibilityProvider: new LoadedSciptsAccessibilityProvider(), @@ -573,7 +594,7 @@ interface ILoadedScriptsItemTemplateData { label: IResourceLabel; } -class LoadedScriptsRenderer implements ITreeRenderer { +class LoadedScriptsRenderer implements ICompressibleTreeRenderer { static readonly ID = 'lsrenderer'; @@ -594,9 +615,23 @@ class LoadedScriptsRenderer implements ITreeRenderer, index: number, data: ILoadedScriptsItemTemplateData): void { const element = node.element; + const label = element.getLabel(); + + this.render(element, label, data, node.filterData); + } + + renderCompressedElements(node: ITreeNode, FuzzyScore>, index: number, data: ILoadedScriptsItemTemplateData, height: number | undefined): void { + + const element = node.element.elements[node.element.elements.length - 1]; + const labels = node.element.elements.map(e => e.getLabel()); + + this.render(element, labels, data, node.filterData); + } + + private render(element: BaseTreeItem, labels: string | string[], data: ILoadedScriptsItemTemplateData, filterData: FuzzyScore | undefined) { const label: IResourceLabelProps = { - name: element.getLabel() + name: labels }; const options: IResourceLabelOptions = { title: element.getHoverLabel() @@ -621,7 +656,7 @@ class LoadedScriptsRenderer implements ITreeRenderer Date: Wed, 15 Jan 2020 11:25:46 +0100 Subject: [PATCH 318/843] :lipstick: --- .../workbench/contrib/userDataSync/browser/userDataSync.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 1a03b92a166..6291311c69d 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -417,7 +417,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (hasRemote && !hasPreviouslySynced) { const result = await this.dialogService.show( Severity.Info, - localize('firs time sync', "First time synchronization"), + localize('firs time sync', "First time Sync"), [ localize('continue', "Continue"), localize('cancel', "Cancel"), @@ -426,7 +426,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo ], { cancelId: 1, - detail: localize('first time sync detail', "Synchronising from this device for the first time. Would you like to \nDownload and replace with the remote data or \nUpload from this device and replace the remote data?") + detail: localize('first time sync detail', "Synchronising from this device for the first time. Would you like to \nDownload and replace with the data from cloud or \nUpload and replace with the data from this device?") } ); @@ -446,7 +446,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo detail: localize('turn off sync detail', "Your settings, keybindings, extensions and more will no longer be synced."), primaryButton: localize('turn off', "Turn off"), checkbox: { - label: localize('turn off sync everywhere', "Turn off sync in all your devices and clear the data from cloud.") + label: localize('turn off sync everywhere', "Turn off sync in all your devices and clear the data in cloud.") } }); if (result.confirmed) { From e193cb5bfe0123787ab8399e31a05d1903d14865 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 15 Jan 2020 11:34:56 +0100 Subject: [PATCH 319/843] Implement visibility change --- .../editor/contrib/suggest/media/suggest.css | 26 +++++++++++++++---- .../editor/contrib/suggest/suggestWidget.ts | 2 ++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index 5e7b46ffba1..63d54498c2b 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -143,19 +143,35 @@ display: inline; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore, +/** Details: if using CompletionItem#details, show on focus **/ + .monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .details-label, -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .details-label, -.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .details-label { display: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, .monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .details-label { display: inline; } +/** Details: if using CompletionItemLabel#details, always show **/ + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .details-label.always-show, +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .details-label.always-show { + display: inline; +} + +/** ReadMore: always layout, only shown on focus **/ + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore { + display: inline; + visibility: hidden; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { + visibility: visible; +} + /** Styles for each row in the list **/ .monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated { diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index b0b1b7ee20b..2dfa7201a3e 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -218,8 +218,10 @@ class Renderer implements IListRenderer data.iconLabel.setLabel(textLabel, undefined, labelOptions); if (typeof suggestion.label === 'string') { data.detailsLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); + removeClass(data.detailsLabel, 'always-show'); } else { data.detailsLabel.textContent = (suggestion.label.details || '').replace(/\n.*$/m, ''); + addClass(data.detailsLabel, 'always-show'); } if (canExpandCompletionItem(element)) { From d5b37e30f4bfab824d8d11b902552877d1dd609e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jan 2020 11:20:10 +0100 Subject: [PATCH 320/843] add `registerAction2` to register a "modern" action --- src/vs/platform/actions/common/actions.ts | 49 ++++++++- .../bulkEdit/browser/bulkEdit.contribution.ts | 104 +++++++++++------- 2 files changed, 109 insertions(+), 44 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 13b05bd3949..24251a117cd 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -5,11 +5,11 @@ import { Action } from 'vs/base/common/actions'; import { SyncDescriptor0, createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { IConstructorSignature2, createDecorator, BrandedService } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IConstructorSignature2, createDecorator, BrandedService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindings, KeybindingsRegistry, IKeybindingRule } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; @@ -402,3 +402,46 @@ export function registerAction(desc: IActionDescriptor) { }); } } + + +//#region --- IAction2 + +export interface IAction2Description extends ICommandAction { + f1?: boolean; + menu?: { id: MenuId } & Omit; + keybinding?: Omit; +} + +export interface IAction2 { + readonly desc: IAction2Description; + run(accessor: ServicesAccessor, args: any[]): any; +} + +export function registerAction2(action: IAction2): IDisposable { + const disposables = new DisposableStore(); + + disposables.add(CommandsRegistry.registerCommand({ + id: action.desc.id, + handler: (accessor, ...args) => action.run(accessor, args), + description: undefined, + })); + + if (action.desc.menu) { + disposables.add(MenuRegistry.appendMenuItem(action.desc.menu.id, { command: action.desc, ...action.desc.menu })); + } + + if (action.desc.f1) { + disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: action.desc, ...action.desc })); + } + + if (action.desc.keybinding) { + KeybindingsRegistry.registerKeybindingRule({ + ...action.desc.keybinding, + id: action.desc.id, + when: ContextKeyExpr.and(action.desc.precondition, action.desc.keybinding.when) + }); + } + + return disposables; +} +//#endregion diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index aacc0db7d51..bfbddca19e8 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -18,14 +18,14 @@ import { RawContextKey, IContextKeyService, IContextKey, ContextKeyExpr } from ' import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { BulkEditPreviewProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; -import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { URI } from 'vs/base/common/uri'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId, registerAction2, IAction2 } from 'vs/platform/actions/common/actions'; import { IEditorInput } from 'vs/workbench/common/editor'; +import type { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; function getBulkEditPane(panelService: IPanelService): BulkEditPane | undefined { let view: ViewPane | undefined; @@ -110,57 +110,79 @@ class BulkEditPreviewContribution { } } + // CMD: accept -CommandsRegistry.registerCommand('refactorPreview.apply', accessor => { - const panelService = accessor.get(IPanelService); - const view = getBulkEditPane(panelService); - if (view) { - view.accept(); - } -}); -KeybindingsRegistry.registerKeybindingRule({ - id: 'refactorPreview.apply', - weight: KeybindingWeight.WorkbenchContrib, - when: BulkEditPreviewContribution.ctxEnabled, - primary: KeyMod.Shift + KeyCode.Enter, -}); -MenuRegistry.appendMenuItem(MenuId.ViewTitle, { - command: { +registerAction2(new class ApplyAction implements IAction2 { + + readonly desc = { id: 'refactorPreview.apply', title: { value: localize('apply', "Apply Changes"), original: 'Apply Changes' }, + category: localize('cat', "Refactor Preview"), icon: { id: 'codicon/check' }, - precondition: BulkEditPreviewContribution.ctxEnabled - }, - when: ContextKeyExpr.equals('view', BulkEditPane.ID), - group: 'navigation' + precondition: BulkEditPreviewContribution.ctxEnabled, + menu: { + id: MenuId.ViewTitle, + when: ContextKeyExpr.equals('view', BulkEditPane.ID), + group: 'navigation' + }, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + when: BulkEditPreviewContribution.ctxEnabled, + primary: KeyMod.Shift + KeyCode.Enter, + } + }; + + run(accessor: ServicesAccessor): any { + const panelService = accessor.get(IPanelService); + const view = getBulkEditPane(panelService); + if (view) { + view.accept(); + } + } }); // CMD: discard -CommandsRegistry.registerCommand('refactorPreview.discard', accessor => { - const panelService = accessor.get(IPanelService); - const view = getBulkEditPane(panelService); - if (view) { - view.discard(); - } -}); -MenuRegistry.appendMenuItem(MenuId.ViewTitle, { - command: { +registerAction2(new class DiscardAction implements IAction2 { + + readonly desc = { id: 'refactorPreview.discard', title: { value: localize('Discard', "Discard Changes"), original: 'Discard Changes' }, + category: localize('cat', "Refactor Preview"), icon: { id: 'codicon/clear-all' }, - precondition: BulkEditPreviewContribution.ctxEnabled - }, - when: ContextKeyExpr.equals('view', BulkEditPane.ID), - group: 'navigation' + precondition: BulkEditPreviewContribution.ctxEnabled, + menu: { + id: MenuId.ViewTitle, + when: ContextKeyExpr.equals('view', BulkEditPane.ID), + group: 'navigation' + } + }; + + run(accessor: ServicesAccessor): void | Promise { + const panelService = accessor.get(IPanelService); + const view = getBulkEditPane(panelService); + if (view) { + view.discard(); + } + } }); + // CMD: toggle -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'refactorPreview.toggleCheckedState', - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(BulkEditPreviewContribution.ctxEnabled, WorkbenchListFocusContextKey), - primary: KeyCode.Space, - handler(accessor) { +registerAction2(new class ToggleAction implements IAction2 { + + readonly desc = { + id: 'refactorPreview.toggleCheckedState', + title: { value: localize('toogleSelection', "Check/Uncheck change"), original: 'Check/Uncheck change' }, + category: localize('cat', "Refactor Preview"), + precondition: BulkEditPreviewContribution.ctxEnabled, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyCode.Space, + } + }; + + run(accessor: ServicesAccessor): void | Promise { const panelService = accessor.get(IPanelService); const view = getBulkEditPane(panelService); if (view) { From ebcd231a510f7dd82d48abb564965d115eb41658 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jan 2020 11:42:47 +0100 Subject: [PATCH 321/843] remove registerAction infavor of `registerAction2` --- src/vs/platform/actions/common/actions.ts | 67 +------- .../browser/extensions.contribution.ts | 61 ++++--- .../markers/browser/markers.contribution.ts | 160 ++++++++++-------- .../output/browser/output.contribution.ts | 34 ++-- .../electron-browser/remote.contribution.ts | 30 ++-- 5 files changed, 155 insertions(+), 197 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 24251a117cd..0146ed8c019 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -8,7 +8,7 @@ import { SyncDescriptor0, createSyncDescriptor } from 'vs/platform/instantiation import { IConstructorSignature2, createDecorator, BrandedService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindings, KeybindingsRegistry, IKeybindingRule } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ICommandService, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -343,67 +343,6 @@ export class SyncActionDescriptor { } } - -export interface IActionDescriptor { - id: string; - handler: ICommandHandler; - - // ICommandUI - title?: ILocalizedString; - category?: string; - f1?: boolean; - - // - menu?: { - menuId: MenuId, - when?: ContextKeyExpr; - group?: string; - }; - - // - keybinding?: { - when?: ContextKeyExpr; - weight?: number; - keys: IKeybindings; - }; -} - - -export function registerAction(desc: IActionDescriptor) { - - const { id, handler, title, category, menu, keybinding } = desc; - - // 1) register as command - CommandsRegistry.registerCommand(id, handler); - - // 2) menus - if (menu && title) { - let command = { id, title, category }; - let { menuId, when, group } = menu; - MenuRegistry.appendMenuItem(menuId, { - command, - when, - group - }); - } - - // 3) keybindings - if (keybinding) { - let { when, weight, keys } = keybinding; - KeybindingsRegistry.registerKeybindingRule({ - id, - when, - weight: weight || 0, - primary: keys.primary, - secondary: keys.secondary, - linux: keys.linux, - mac: keys.mac, - win: keys.win - }); - } -} - - //#region --- IAction2 export interface IAction2Description extends ICommandAction { @@ -414,7 +353,7 @@ export interface IAction2Description extends ICommandAction { export interface IAction2 { readonly desc: IAction2Description; - run(accessor: ServicesAccessor, args: any[]): any; + run(accessor: ServicesAccessor, ...args: any): any; } export function registerAction2(action: IAction2): IDisposable { @@ -422,7 +361,7 @@ export function registerAction2(action: IAction2): IDisposable { disposables.add(CommandsRegistry.registerCommand({ id: action.desc.id, - handler: (accessor, ...args) => action.run(accessor, args), + handler: (accessor, ...args) => action.run(accessor, ...args), description: undefined, })); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 6550640590a..54100b78d50 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor, MenuRegistry, MenuId, registerAction } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionsLabel, ExtensionsChannelId, PreferencesLabel, IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -341,10 +341,16 @@ MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { // Extension Context Menu -registerAction({ - id: 'workbench.extensions.action.copyExtension', - title: { value: localize('workbench.extensions.action.copyExtension', "Copy"), original: 'Copy' }, - async handler(accessor, extensionId: string) { +registerAction2({ + desc: { + id: 'workbench.extensions.action.copyExtension', + title: { value: localize('workbench.extensions.action.copyExtension', "Copy"), original: 'Copy' }, + menu: { + id: MenuId.ExtensionContext, + group: '1_copy' + } + }, + async run(accessor, extensionId: string) { const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); let extension = extensionWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] || (await extensionWorkbenchService.queryGallery({ names: [extensionId], pageSize: 1 }, CancellationToken.None)).firstPage[0]; @@ -358,37 +364,36 @@ registerAction({ const clipboardStr = `${name}\n${id}\n${description}\n${verision}\n${publisher}${link ? '\n' + link : ''}`; await accessor.get(IClipboardService).writeText(clipboardStr); } - }, - menu: { - menuId: MenuId.ExtensionContext, - group: '1_copy' - }, + } }); -registerAction({ - id: 'workbench.extensions.action.copyExtensionId', - title: { value: localize('workbench.extensions.action.copyExtensionId', "Copy Extension Id"), original: 'Copy Extension Id' }, - async handler(accessor, id: string) { +registerAction2({ + desc: { + id: 'workbench.extensions.action.copyExtensionId', + title: { value: localize('workbench.extensions.action.copyExtensionId', "Copy Extension Id"), original: 'Copy Extension Id' }, + menu: { + id: MenuId.ExtensionContext, + group: '1_copy' + } + }, + async run(accessor, id: string) { await accessor.get(IClipboardService).writeText(id); }, - menu: { - menuId: MenuId.ExtensionContext, - group: '1_copy' - }, }); -registerAction({ - id: 'workbench.extensions.action.configure', - title: { value: localize('workbench.extensions.action.configure', "Configure..."), original: 'Configure...' }, - async handler(accessor, id: string) { +registerAction2({ + desc: { + id: 'workbench.extensions.action.configure', + title: { value: localize('workbench.extensions.action.configure', "Configure..."), original: 'Configure...' }, + menu: { + id: MenuId.ExtensionContext, + group: '2_configure', + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasConfiguration')) + } + }, + async run(accessor, id: string) { await accessor.get(IPreferencesService).openSettings(false, `@ext:${id}`); }, - menu: { - menuId: MenuId.ExtensionContext, - group: '2_configure', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasConfiguration')) - }, - }); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index bc3a4821115..415fa4238ca 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -13,7 +13,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; import { Marker, RelatedInformation } from 'vs/workbench/contrib/markers/browser/markersModel'; import { MarkersView, getMarkersView } from 'vs/workbench/contrib/markers/browser/markersView'; -import { MenuId, MenuRegistry, SyncActionDescriptor, registerAction } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, SyncActionDescriptor, registerAction2 } from 'vs/platform/actions/common/actions'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { Registry } from 'vs/platform/registry/common/platform'; import { ShowProblemsPanelAction } from 'vs/workbench/contrib/markers/browser/markersViewActions'; @@ -136,103 +136,113 @@ registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMarkersPanelA primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_M }), 'View: Toggle Problems (Errors, Warnings, Infos)', Messages.MARKERS_PANEL_VIEW_CATEGORY); registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowProblemsPanelAction, ShowProblemsPanelAction.ID, ShowProblemsPanelAction.LABEL), 'View: Focus Problems (Errors, Warnings, Infos)', Messages.MARKERS_PANEL_VIEW_CATEGORY); -registerAction({ - id: Constants.MARKER_COPY_ACTION_ID, - title: { value: localize('copyMarker', "Copy"), original: 'Copy' }, - async handler(accessor) { +registerAction2({ + desc: { + id: Constants.MARKER_COPY_ACTION_ID, + title: { value: localize('copyMarker', "Copy"), original: 'Copy' }, + menu: { + id: MenuId.ProblemsPanelContext, + when: Constants.MarkerFocusContextKey, + group: 'navigation' + }, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + when: Constants.MarkerFocusContextKey + }, + }, + async run(accessor) { await copyMarker(accessor.get(IPanelService), accessor.get(IClipboardService)); }, - menu: { - menuId: MenuId.ProblemsPanelContext, - when: Constants.MarkerFocusContextKey, - group: 'navigation' - }, - keybinding: { - weight: KeybindingWeight.WorkbenchContrib, - keys: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_C - }, - when: Constants.MarkerFocusContextKey - } }); -registerAction({ - id: Constants.MARKER_COPY_MESSAGE_ACTION_ID, - title: { value: localize('copyMessage', "Copy Message"), original: 'Copy Message' }, - async handler(accessor) { +registerAction2({ + desc: { + id: Constants.MARKER_COPY_MESSAGE_ACTION_ID, + title: { value: localize('copyMessage', "Copy Message"), original: 'Copy Message' }, + menu: { + id: MenuId.ProblemsPanelContext, + when: Constants.MarkerFocusContextKey, + group: 'navigation' + }, + }, + async run(accessor) { await copyMessage(accessor.get(IPanelService), accessor.get(IClipboardService)); }, - menu: { - menuId: MenuId.ProblemsPanelContext, - when: Constants.MarkerFocusContextKey, - group: 'navigation' - } }); -registerAction({ - id: Constants.RELATED_INFORMATION_COPY_MESSAGE_ACTION_ID, - title: { value: localize('copyMessage', "Copy Message"), original: 'Copy Message' }, - async handler(accessor) { +registerAction2({ + desc: { + id: Constants.RELATED_INFORMATION_COPY_MESSAGE_ACTION_ID, + title: { value: localize('copyMessage', "Copy Message"), original: 'Copy Message' }, + menu: { + id: MenuId.ProblemsPanelContext, + when: Constants.RelatedInformationFocusContextKey, + group: 'navigation' + } + }, + async run(accessor) { await copyRelatedInformationMessage(accessor.get(IPanelService), accessor.get(IClipboardService)); - }, - menu: { - menuId: MenuId.ProblemsPanelContext, - when: Constants.RelatedInformationFocusContextKey, - group: 'navigation' } }); -registerAction({ - id: Constants.FOCUS_PROBLEMS_FROM_FILTER, - handler(accessor) { - focusProblemsView(accessor.get(IPanelService)); - }, - keybinding: { - when: Constants.MarkerPanelFilterFocusContextKey, - weight: KeybindingWeight.WorkbenchContrib, - keys: { +registerAction2({ + desc: { + id: Constants.FOCUS_PROBLEMS_FROM_FILTER, + title: localize('focusProblemsList', "Focus problems view"), + keybinding: { + when: Constants.MarkerPanelFilterFocusContextKey, + weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.DownArrow - }, - } -}); -registerAction({ - id: Constants.MARKERS_PANEL_FOCUS_FILTER, - handler(accessor) { - focusProblemsFilter(accessor.get(IPanelService)); + } }, - keybinding: { - when: Constants.MarkerPanelFocusContextKey, - weight: KeybindingWeight.WorkbenchContrib, - keys: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_F - }, + run(accessor) { + focusProblemsView(accessor.get(IPanelService)); } }); -registerAction({ - id: Constants.MARKERS_PANEL_SHOW_MULTILINE_MESSAGE, - handler(accessor) { +registerAction2({ + desc: { + id: Constants.MARKERS_PANEL_FOCUS_FILTER, + title: localize('focusProblemsFilter', "Focus problems filter"), + keybinding: { + when: Constants.MarkerPanelFocusContextKey, + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyCode.KEY_F + } + }, + run(accessor) { + focusProblemsFilter(accessor.get(IPanelService)); + } +}); +registerAction2({ + desc: { + id: Constants.MARKERS_PANEL_SHOW_MULTILINE_MESSAGE, + title: { value: localize('show multiline', "Show message in multiple lines"), original: 'Problems: Show message in multiple lines' }, + category: localize('problems', "Problems"), + menu: { + id: MenuId.CommandPalette, + when: ActivePanelContext.isEqualTo(Constants.MARKERS_PANEL_ID) + } + }, + run(accessor) { const markersView = getMarkersView(accessor.get(IPanelService)); if (markersView) { markersView.markersViewModel.multiline = true; } - }, - title: { value: localize('show multiline', "Show message in multiple lines"), original: 'Problems: Show message in multiple lines' }, - category: localize('problems', "Problems"), - menu: { - menuId: MenuId.CommandPalette, - when: ActivePanelContext.isEqualTo(Constants.MARKERS_PANEL_ID) } }); -registerAction({ - id: Constants.MARKERS_PANEL_SHOW_SINGLELINE_MESSAGE, - handler(accessor) { +registerAction2({ + desc: { + id: Constants.MARKERS_PANEL_SHOW_SINGLELINE_MESSAGE, + title: { value: localize('show singleline', "Show message in single line"), original: 'Problems: Show message in single line' }, + category: localize('problems', "Problems"), + menu: { + id: MenuId.CommandPalette, + when: ActivePanelContext.isEqualTo(Constants.MARKERS_PANEL_ID) + } + }, + run(accessor) { const markersView = getMarkersView(accessor.get(IPanelService)); if (markersView) { markersView.markersViewModel.multiline = false; } - }, - title: { value: localize('show singleline', "Show message in single line"), original: 'Problems: Show message in single line' }, - category: localize('problems', "Problems"), - menu: { - menuId: MenuId.CommandPalette, - when: ActivePanelContext.isEqualTo(Constants.MARKERS_PANEL_ID) } }); diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index ec68b3b8556..13a9fd16f1f 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { MenuId, MenuRegistry, SyncActionDescriptor, registerAction } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, SyncActionDescriptor, registerAction2 } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { OutputService, LogContentProvider } from 'vs/workbench/contrib/output/browser/outputServices'; @@ -89,14 +89,16 @@ actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ShowLogsOutpu actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(OpenOutputLogFileAction, OpenOutputLogFileAction.ID, OpenOutputLogFileAction.LABEL), 'Developer: Open Log File...', devCategory); // Define clear command, contribute to editor context menu -registerAction({ - id: 'editor.action.clearoutput', - title: { value: nls.localize('clearOutput.label', "Clear Output"), original: 'Clear Output' }, - menu: { - menuId: MenuId.EditorContext, - when: CONTEXT_IN_OUTPUT +registerAction2({ + desc: { + id: 'editor.action.clearoutput', + title: { value: nls.localize('clearOutput.label', "Clear Output"), original: 'Clear Output' }, + menu: { + id: MenuId.EditorContext, + when: CONTEXT_IN_OUTPUT + }, }, - handler(accessor) { + run(accessor) { const activeChannel = accessor.get(IOutputService).getActiveChannel(); if (activeChannel) { activeChannel.clear(); @@ -104,14 +106,16 @@ registerAction({ } }); -registerAction({ - id: 'workbench.action.openActiveLogOutputFile', - title: { value: nls.localize('openActiveLogOutputFile', "Open Active Log Output File"), original: 'Open Active Log Output File' }, - menu: { - menuId: MenuId.CommandPalette, - when: CONTEXT_ACTIVE_LOG_OUTPUT +registerAction2({ + desc: { + id: 'workbench.action.openActiveLogOutputFile', + title: { value: nls.localize('openActiveLogOutputFile', "Open Active Log Output File"), original: 'Open Active Log Output File' }, + menu: { + id: MenuId.CommandPalette, + when: CONTEXT_ACTIVE_LOG_OUTPUT + }, }, - handler(accessor) { + run(accessor) { accessor.get(IInstantiationService).createInstance(OpenLogOutputFile).run(); } }); diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index 8de660c78fe..965821ee7bd 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -12,7 +12,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry, registerAction } from 'vs/platform/actions/common/actions'; +import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchContributionsExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar'; @@ -71,28 +71,28 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc const category = nls.localize('remote.category', "Remote"); - registerAction({ - id: WINDOW_ACTIONS_COMMAND_ID, - category, - title: { value: nls.localize('remote.showMenu', "Show Remote Menu"), original: 'Show Remote Menu' }, - menu: { - menuId: MenuId.CommandPalette + registerAction2({ + desc: { + id: WINDOW_ACTIONS_COMMAND_ID, + category, + title: { value: nls.localize('remote.showMenu', "Show Remote Menu"), original: 'Show Remote Menu' }, + f1: true, }, - handler: (_accessor) => this.showIndicatorActions(this.windowCommandMenu) + run: () => this.showIndicatorActions(this.windowCommandMenu) }); this.remoteAuthority = environmentService.configuration.remoteAuthority; Deprecated_RemoteAuthorityContext.bindTo(this.contextKeyService).set(this.remoteAuthority || ''); if (this.remoteAuthority) { - registerAction({ - id: CLOSE_REMOTE_COMMAND_ID, - category, - title: { value: nls.localize('remote.close', "Close Remote Connection"), original: 'Close Remote Connection' }, - menu: { - menuId: MenuId.CommandPalette + registerAction2({ + desc: { + id: CLOSE_REMOTE_COMMAND_ID, + category, + title: { value: nls.localize('remote.close', "Close Remote Connection"), original: 'Close Remote Connection' }, + f1: true }, - handler: (_accessor) => this.remoteAuthority && hostService.openWindow({ forceReuseWindow: true }) + run: () => this.remoteAuthority && hostService.openWindow({ forceReuseWindow: true }) }); // Pending entry until extensions are ready From 9e13959d4c81be7b7a40195bca650893bed71d91 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jan 2020 12:02:02 +0100 Subject: [PATCH 322/843] actually use opacity for the label..., #88552 --- src/vs/editor/contrib/rename/renameInputField.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/editor/contrib/rename/renameInputField.css b/src/vs/editor/contrib/rename/renameInputField.css index a61b3741c2f..1a6d7a75ec0 100644 --- a/src/vs/editor/contrib/rename/renameInputField.css +++ b/src/vs/editor/contrib/rename/renameInputField.css @@ -13,3 +13,7 @@ padding: 4px; width: calc(100% - 8px); } + +.monaco-editor .rename-box .rename-label { + opacity: .8; +} From a78d0d7b862ca8a8e662df2fe3aa95b6146a112b Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 15 Jan 2020 12:11:14 +0100 Subject: [PATCH 323/843] fixes #88645 --- .../contrib/debug/browser/breakpointEditorContribution.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 83b983abb81..36fc965ca8f 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -468,6 +468,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi const newBreakpointRange = model.getDecorationRange(breakpointDecoration.decorationId); if (newBreakpointRange && (!breakpointDecoration.range.equalsRange(newBreakpointRange))) { somethingChanged = true; + breakpointDecoration.range = newBreakpointRange; } }); if (!somethingChanged) { From c2e5e850900f0e7e736d63b162a5e3ae54105acd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jan 2020 12:15:37 +0100 Subject: [PATCH 324/843] IAction2 - allow multiple menu items --- src/vs/platform/actions/common/actions.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 0146ed8c019..e5956e0b105 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -345,9 +345,11 @@ export class SyncActionDescriptor { //#region --- IAction2 +type OneOrN = T | T[]; + export interface IAction2Description extends ICommandAction { f1?: boolean; - menu?: { id: MenuId } & Omit; + menu?: OneOrN<{ id: MenuId } & Omit>; keybinding?: Omit; } @@ -365,7 +367,11 @@ export function registerAction2(action: IAction2): IDisposable { description: undefined, })); - if (action.desc.menu) { + if (Array.isArray(action.desc.menu)) { + for (let item of action.desc.menu) { + disposables.add(MenuRegistry.appendMenuItem(item.id, { command: action.desc, ...item })); + } + } else if (action.desc.menu) { disposables.add(MenuRegistry.appendMenuItem(action.desc.menu.id, { command: action.desc, ...action.desc.menu })); } From a3a53d730ce6b379e5e269869d1b2b19b7c1376d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jan 2020 12:46:03 +0100 Subject: [PATCH 325/843] add context menu commands to bulk edit pane --- src/vs/platform/actions/common/actions.ts | 1 + .../bulkEdit/browser/bulkEdit.contribution.ts | 19 +++++++--- .../contrib/bulkEdit/browser/bulkEditPane.ts | 38 +++++++++++++++++-- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index e5956e0b105..7c036d9278c 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -108,6 +108,7 @@ export const enum MenuId { CommentThreadActions, CommentTitle, CommentActions, + BulkEditPaneContext, } export interface IMenuActionOptions { diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index bfbddca19e8..ac1c9ce89e4 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -120,11 +120,13 @@ registerAction2(new class ApplyAction implements IAction2 { category: localize('cat', "Refactor Preview"), icon: { id: 'codicon/check' }, precondition: BulkEditPreviewContribution.ctxEnabled, - menu: { + menu: [{ id: MenuId.ViewTitle, when: ContextKeyExpr.equals('view', BulkEditPane.ID), group: 'navigation' - }, + }, { + id: MenuId.BulkEditPaneContext, + }], keybinding: { weight: KeybindingWeight.WorkbenchContrib, when: BulkEditPreviewContribution.ctxEnabled, @@ -150,11 +152,13 @@ registerAction2(new class DiscardAction implements IAction2 { category: localize('cat', "Refactor Preview"), icon: { id: 'codicon/clear-all' }, precondition: BulkEditPreviewContribution.ctxEnabled, - menu: { + menu: [{ id: MenuId.ViewTitle, when: ContextKeyExpr.equals('view', BulkEditPane.ID), group: 'navigation' - } + }, { + id: MenuId.BulkEditPaneContext + }] }; run(accessor: ServicesAccessor): void | Promise { @@ -172,13 +176,18 @@ registerAction2(new class ToggleAction implements IAction2 { readonly desc = { id: 'refactorPreview.toggleCheckedState', - title: { value: localize('toogleSelection', "Check/Uncheck change"), original: 'Check/Uncheck change' }, + title: { value: localize('toogleSelection', "Accept Change"), original: 'Accept Change' }, category: localize('cat', "Refactor Preview"), precondition: BulkEditPreviewContribution.ctxEnabled, + toggled: BulkEditPane.ctxChangeChecked, keybinding: { weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: KeyCode.Space, + }, + menu: { + id: MenuId.BulkEditPaneContext, + group: 'navigation' } }; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 44f82cbc7c6..53513f38d55 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -22,12 +22,16 @@ import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { basename } from 'vs/base/common/resources'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import type { IAction } from 'vs/base/common/actions'; +import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import type { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; const enum State { Data = 'data', @@ -37,12 +41,15 @@ const enum State { export class BulkEditPane extends ViewPane { static readonly ID = 'refactorPreview'; + static readonly ctxChangeChecked = new RawContextKey('refactorPreview.changeChecked', true); private _tree!: WorkbenchAsyncDataTree; private _message!: HTMLSpanElement; private readonly _disposables = new DisposableStore(); + private readonly _ctxElementChecked: IContextKey; + private readonly _sessionDisposables = new DisposableStore(); private _currentResolve?: (edit?: WorkspaceEdit) => void; private _currentInput?: BulkFileOperations; @@ -54,17 +61,20 @@ export class BulkEditPane extends ViewPane { @ILabelService private readonly _labelService: ILabelService, @ITextModelService private readonly _textModelService: ITextModelService, @IDialogService private readonly _dialogService: IDialogService, + @IMenuService private readonly _menuService: IMenuService, + @IContextMenuService private readonly _contextMenuService: IContextMenuService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, - @IContextKeyService contextKeyService: IContextKeyService ) { super( options, - keybindingService, contextMenuService, configurationService, contextKeyService, _instaService + keybindingService, contextMenuService, configurationService, _contextKeyService, _instaService ); this.element.classList.add('bulk-edit-panel', 'show-file-icons'); + this._ctxElementChecked = BulkEditPane.ctxChangeChecked.bindTo(_contextKeyService); } dispose(): void { @@ -110,6 +120,13 @@ export class BulkEditPane extends ViewPane { } })); + this._disposables.add(this._tree.onDidChangeFocus(e => { + const [first] = e.elements; + this._ctxElementChecked.set(first && first.edit.isChecked()); + })); + + this._disposables.add(this._tree.onContextMenu(this._onContextMenu, this)); + // message this._message = document.createElement('span'); this._message.className = 'message'; @@ -251,6 +268,21 @@ export class BulkEditPane extends ViewPane { }); } } + + private _onContextMenu(e: ITreeContextMenuEvent): void { + const menu = this._menuService.createMenu(MenuId.BulkEditPaneContext, this._contextKeyService); + const actions: IAction[] = []; + const disposable = createAndFillInContextMenuActions(menu, undefined, actions, this._contextMenuService); + + this._contextMenuService.showContextMenu({ + getActions: () => actions, + getAnchor: () => e.anchor!, + onHide: () => { + disposable.dispose(); + menu.dispose(); + } + }); + } } registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { From 2585092dbeba507926da895b3a27a59585c172f0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jan 2020 12:56:15 +0100 Subject: [PATCH 326/843] tests - reduce output of integration tests (#84283) --- .../workspace.event.test.ts | 6 +-- .../src/singlefolder-tests/workspace.test.ts | 46 +++++++++---------- extensions/vscode-api-tests/src/utils.ts | 13 ++++++ src/vs/code/electron-main/window.ts | 8 ++-- src/vs/platform/log/common/logIpc.ts | 2 + .../electron-main/abstractUpdateService.ts | 4 ++ src/vs/workbench/api/common/apiCommands.ts | 17 +++++++ .../electron-browser/webviewProtocols.ts | 4 -- .../extensions/common/extensionDevOptions.ts | 2 +- .../electron-browser/extensionHost.ts | 38 +++++++-------- 10 files changed, 87 insertions(+), 53 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.event.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.event.test.ts index ea71c64485d..ce6274157a1 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.event.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.event.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile } from '../utils'; +import { createRandomFile, withLogDisabled } from '../utils'; suite('workspace-event', () => { @@ -66,7 +66,7 @@ suite('workspace-event', () => { assert.equal(baseDoc.getText(), 'HALLO_NEW'); }); - test('onWillCreate/onDidCreate, make changes, edit new file fails', async function () { + test('onWillCreate/onDidCreate, make changes, edit new file fails', withLogDisabled(async function () { const base = await createRandomFile(); @@ -86,7 +86,7 @@ suite('workspace-event', () => { assert.equal((await vscode.workspace.fs.readFile(newUri)).toString(), ''); assert.equal((await vscode.workspace.openTextDocument(newUri)).getText(), ''); - }); + })); test('onWillDelete/onDidDelete', async function () { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 6e409489af6..20e6be79d62 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile, deleteFile, closeAllEditors, pathEquals, rndName, disposeAll, testFs, delay } from '../utils'; +import { createRandomFile, deleteFile, closeAllEditors, pathEquals, rndName, disposeAll, testFs, delay, withLogDisabled } from '../utils'; import { join, posix, basename } from 'path'; import * as fs from 'fs'; @@ -598,7 +598,7 @@ suite('workspace-namespace', () => { assert.equal(doc.isDirty, true); }); - test('applyEdit should fail when editing deleted resource', async () => { + test('applyEdit should fail when editing deleted resource', withLogDisabled(async () => { const resource = await createRandomFile(); const edit = new vscode.WorkspaceEdit(); @@ -607,9 +607,9 @@ suite('workspace-namespace', () => { let success = await vscode.workspace.applyEdit(edit); assert.equal(success, false); - }); + })); - test('applyEdit should fail when renaming deleted resource', async () => { + test('applyEdit should fail when renaming deleted resource', withLogDisabled(async () => { const resource = await createRandomFile(); const edit = new vscode.WorkspaceEdit(); @@ -618,9 +618,9 @@ suite('workspace-namespace', () => { let success = await vscode.workspace.applyEdit(edit); assert.equal(success, false); - }); + })); - test('applyEdit should fail when editing renamed from resource', async () => { + test('applyEdit should fail when editing renamed from resource', withLogDisabled(async () => { const resource = await createRandomFile(); const newResource = vscode.Uri.file(resource.fsPath + '.1'); const edit = new vscode.WorkspaceEdit(); @@ -629,7 +629,7 @@ suite('workspace-namespace', () => { let success = await vscode.workspace.applyEdit(edit); assert.equal(success, false); - }); + })); test('applyEdit "edit A -> rename A to B -> edit B"', async () => { await testEditRenameEdit(oldUri => oldUri.with({ path: oldUri.path + 'NEW' })); @@ -662,7 +662,7 @@ suite('workspace-namespace', () => { return uri.with({ path: posix.join(posix.dirname(uri.path), `_${posix.basename(uri.path)}`) }); } - test('WorkspaceEdit: applying edits before and after rename duplicates resource #42633', async function () { + test('WorkspaceEdit: applying edits before and after rename duplicates resource #42633', withLogDisabled(async function () { let docUri = await createRandomFile(); let newUri = nameWithUnderscore(docUri); @@ -675,9 +675,9 @@ suite('workspace-namespace', () => { assert.ok(await vscode.workspace.applyEdit(we)); let doc = await vscode.workspace.openTextDocument(newUri); assert.equal(doc.getText(), 'BarHelloFoo'); - }); + })); - test('WorkspaceEdit: Problem recreating a renamed resource #42634', async function () { + test('WorkspaceEdit: Problem recreating a renamed resource #42634', withLogDisabled(async function () { let docUri = await createRandomFile(); let newUri = nameWithUnderscore(docUri); @@ -695,9 +695,9 @@ suite('workspace-namespace', () => { assert.equal(newDoc.getText(), 'HelloFoo'); let doc = await vscode.workspace.openTextDocument(docUri); assert.equal(doc.getText(), 'Bar'); - }); + })); - test('WorkspaceEdit api - after saving a deleted file, it still shows up as deleted. #42667', async function () { + test('WorkspaceEdit api - after saving a deleted file, it still shows up as deleted. #42667', withLogDisabled(async function () { let docUri = await createRandomFile(); let we = new vscode.WorkspaceEdit(); we.deleteFile(docUri); @@ -710,7 +710,7 @@ suite('workspace-namespace', () => { } catch (e) { assert.ok(true); } - }); + })); test('WorkspaceEdit: edit and rename parent folder duplicates resource #42641', async function () { @@ -741,7 +741,7 @@ suite('workspace-namespace', () => { assert.equal(doc.getText(), 'Hello'); }); - test('WorkspaceEdit: rename resource followed by edit does not work #42638', async function () { + test('WorkspaceEdit: rename resource followed by edit does not work #42638', withLogDisabled(async function () { let docUri = await createRandomFile(); let newUri = nameWithUnderscore(docUri); @@ -753,9 +753,9 @@ suite('workspace-namespace', () => { let doc = await vscode.workspace.openTextDocument(newUri); assert.equal(doc.getText(), 'Hello'); - }); + })); - test('WorkspaceEdit: create & override', async function () { + test('WorkspaceEdit: create & override', withLogDisabled(async function () { let docUri = await createRandomFile('before'); @@ -768,9 +768,9 @@ suite('workspace-namespace', () => { we.createFile(docUri, { overwrite: true }); assert.ok(await vscode.workspace.applyEdit(we)); assert.equal((await vscode.workspace.openTextDocument(docUri)).getText(), ''); - }); + })); - test('WorkspaceEdit: create & ignoreIfExists', async function () { + test('WorkspaceEdit: create & ignoreIfExists', withLogDisabled(async function () { let docUri = await createRandomFile('before'); let we = new vscode.WorkspaceEdit(); @@ -782,9 +782,9 @@ suite('workspace-namespace', () => { we.createFile(docUri, { overwrite: true, ignoreIfExists: true }); assert.ok(await vscode.workspace.applyEdit(we)); assert.equal((await vscode.workspace.openTextDocument(docUri)).getText(), ''); - }); + })); - test('WorkspaceEdit: rename & ignoreIfExists', async function () { + test('WorkspaceEdit: rename & ignoreIfExists', withLogDisabled(async function () { let aUri = await createRandomFile('aaa'); let bUri = await createRandomFile('bbb'); @@ -803,9 +803,9 @@ suite('workspace-namespace', () => { we = new vscode.WorkspaceEdit(); we.renameFile(aUri, bUri, { overwrite: true, ignoreIfExists: true }); assert.ok(await vscode.workspace.applyEdit(we)); - }); + })); - test('WorkspaceEdit: delete & ignoreIfNotExists', async function () { + test('WorkspaceEdit: delete & ignoreIfNotExists', withLogDisabled(async function () { let docUri = await createRandomFile(); let we = new vscode.WorkspaceEdit(); @@ -819,7 +819,7 @@ suite('workspace-namespace', () => { we = new vscode.WorkspaceEdit(); we.deleteFile(docUri, { ignoreIfNotExists: true }); assert.ok(await vscode.workspace.applyEdit(we)); - }); + })); test('WorkspaceEdit: insert & rename multiple', async function () { diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index 0058c215f87..e270cd73adf 100644 --- a/extensions/vscode-api-tests/src/utils.ts +++ b/extensions/vscode-api-tests/src/utils.ts @@ -70,3 +70,16 @@ function isTestTypeActive(): boolean { export function delay(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } + +export function withLogDisabled(runnable: () => Promise): () => Promise { + return async (): Promise => { + const logLevel = await vscode.commands.executeCommand('_extensionTests.getLogLevel'); + await vscode.commands.executeCommand('_extensionTests.setLogLevel', 6 /* critical */); + + try { + await runnable(); + } finally { + await vscode.commands.executeCommand('_extensionTests.setLogLevel', logLevel); + } + }; +} diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index fadf9730016..af00dd94a84 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -1007,22 +1007,22 @@ export class CodeWindow extends Disposable implements ICodeWindow { switch (visibility) { case ('default'): this._win.setMenuBarVisibility(!isFullscreen); - this._win.setAutoHideMenuBar(isFullscreen); + this._win.autoHideMenuBar = isFullscreen; break; case ('visible'): this._win.setMenuBarVisibility(true); - this._win.setAutoHideMenuBar(false); + this._win.autoHideMenuBar = false; break; case ('toggle'): this._win.setMenuBarVisibility(false); - this._win.setAutoHideMenuBar(true); + this._win.autoHideMenuBar = true; break; case ('hidden'): this._win.setMenuBarVisibility(false); - this._win.setAutoHideMenuBar(false); + this._win.autoHideMenuBar = false; break; } } diff --git a/src/vs/platform/log/common/logIpc.ts b/src/vs/platform/log/common/logIpc.ts index 260922cedcb..d12b3f48c77 100644 --- a/src/vs/platform/log/common/logIpc.ts +++ b/src/vs/platform/log/common/logIpc.ts @@ -77,6 +77,8 @@ export class FollowerLogService extends DelegatedLogService implements ILogServi } setLevel(level: LogLevel): void { + super.setLevel(level); + this.master.setLevel(level); } } diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index 8a1c95d37be..d8bf464fed9 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -50,6 +50,10 @@ export abstract class AbstractUpdateService implements IUpdateService { @IRequestService protected requestService: IRequestService, @ILogService protected logService: ILogService, ) { + if (!this.environmentService.isBuilt) { + return; // updates are never enabled when running out of sources + } + if (this.environmentService.disableUpdates) { this.logService.info('update#ctor - updates are disabled by the environment'); return; diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index 69fc23a9af1..ee5b836f009 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -14,6 +14,8 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { IWorkspacesService, hasWorkspaceFileExtension, IRecent } from 'vs/platform/workspaces/common/workspaces'; import { Schemas } from 'vs/base/common/network'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; // ----------------------------------------------------------------- // The following commands are registered on both sides separately. @@ -237,3 +239,18 @@ CommandsRegistry.registerCommand({ }] } }); + +CommandsRegistry.registerCommand('_extensionTests.setLogLevel', function (accessor: ServicesAccessor, level: number) { + const logService = accessor.get(ILogService); + const environmentService = accessor.get(IEnvironmentService); + + if (environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI) { + logService.setLevel(level); + } +}); + +CommandsRegistry.registerCommand('_extensionTests.getLogLevel', function (accessor: ServicesAccessor) { + const logService = accessor.get(ILogService); + + return logService.getLevel(); +}); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts index fd5ae829c99..e84ce9bf6fb 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts @@ -32,10 +32,6 @@ export function registerFileProtocol( } return callback({ error: -2 /* FAILED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); - }, (error) => { - if (error) { - console.error(`Failed to register '${protocol}' protocol`); - } }); } diff --git a/src/vs/workbench/services/extensions/common/extensionDevOptions.ts b/src/vs/workbench/services/extensions/common/extensionDevOptions.ts index ba9a9ba9fee..436f3e98773 100644 --- a/src/vs/workbench/services/extensions/common/extensionDevOptions.ts +++ b/src/vs/workbench/services/extensions/common/extensionDevOptions.ts @@ -34,6 +34,6 @@ export function parseExtensionDevOptions(environmentService: IEnvironmentService isExtensionDevHost, isExtensionDevDebug, isExtensionDevDebugBrk, - isExtensionDevTestFromCli, + isExtensionDevTestFromCli }; } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index b009292b709..c45f7e5c3e3 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -199,7 +199,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { onDebouncedOutput(output => { const inspectorUrlMatch = output.data && output.data.match(/ws:\/\/([^\s]+:(\d+)\/[^\s]+)/); if (inspectorUrlMatch) { - if (!this._environmentService.isBuilt) { + if (!this._environmentService.isBuilt && !this._isExtensionDevTestFromCli) { console.log(`%c[Extension Host] %cdebugger inspector at chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=${inspectorUrlMatch[1]}`, 'color: blue', 'color:'); } if (!this._inspectPort) { @@ -207,9 +207,11 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { this._onDidSetInspectPort.fire(); } } else { - console.group('Extension Host'); - console.log(output.data, ...output.format); - console.groupEnd(); + if (!this._isExtensionDevTestFromCli) { + console.group('Extension Host'); + console.log(output.data, ...output.format); + console.groupEnd(); + } } }); @@ -292,22 +294,22 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { const expected = this._environmentService.debugExtensionHost.port; const port = await findFreePort(expected, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */); - if (!port) { - console.warn('%c[Extension Host] %cCould not find a free port for debugging', 'color: blue', 'color:'); - return 0; + if (!this._isExtensionDevTestFromCli) { + if (!port) { + console.warn('%c[Extension Host] %cCould not find a free port for debugging', 'color: blue', 'color:'); + } else { + if (port !== expected) { + console.warn(`%c[Extension Host] %cProvided debugging port ${expected} is not free, using ${port} instead.`, 'color: blue', 'color:'); + } + if (this._isExtensionDevDebugBrk) { + console.warn(`%c[Extension Host] %cSTOPPED on first line for debugging on port ${port}`, 'color: blue', 'color:'); + } else { + console.info(`%c[Extension Host] %cdebugger listening on port ${port}`, 'color: blue', 'color:'); + } + } } - if (port !== expected) { - console.warn(`%c[Extension Host] %cProvided debugging port ${expected} is not free, using ${port} instead.`, 'color: blue', 'color:'); - } - if (this._isExtensionDevDebugBrk) { - console.warn(`%c[Extension Host] %cSTOPPED on first line for debugging on port ${port}`, 'color: blue', 'color:'); - } else { - console.info(`%c[Extension Host] %cdebugger listening on port ${port}`, 'color: blue', 'color:'); - } - return port; - - + return port || 0; } private _tryExtHostHandshake(): Promise { From 2af91664d482c6065f7a0d589823ec115a65ec10 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jan 2020 12:50:48 +0100 Subject: [PATCH 327/843] assign order to commands --- .../contrib/bulkEdit/browser/bulkEdit.contribution.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index ac1c9ce89e4..8e1ec18afc5 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -116,7 +116,7 @@ registerAction2(new class ApplyAction implements IAction2 { readonly desc = { id: 'refactorPreview.apply', - title: { value: localize('apply', "Apply Changes"), original: 'Apply Changes' }, + title: { value: localize('apply', "Apply Refactoring"), original: 'Apply Refactoring' }, category: localize('cat', "Refactor Preview"), icon: { id: 'codicon/check' }, precondition: BulkEditPreviewContribution.ctxEnabled, @@ -126,6 +126,7 @@ registerAction2(new class ApplyAction implements IAction2 { group: 'navigation' }, { id: MenuId.BulkEditPaneContext, + order: 1 }], keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -148,7 +149,7 @@ registerAction2(new class DiscardAction implements IAction2 { readonly desc = { id: 'refactorPreview.discard', - title: { value: localize('Discard', "Discard Changes"), original: 'Discard Changes' }, + title: { value: localize('Discard', "Discard Refactoring"), original: 'Discard Refactoring' }, category: localize('cat', "Refactor Preview"), icon: { id: 'codicon/clear-all' }, precondition: BulkEditPreviewContribution.ctxEnabled, @@ -157,7 +158,8 @@ registerAction2(new class DiscardAction implements IAction2 { when: ContextKeyExpr.equals('view', BulkEditPane.ID), group: 'navigation' }, { - id: MenuId.BulkEditPaneContext + id: MenuId.BulkEditPaneContext, + order: 2 }] }; From cbe793261bed1c811f39e2f6476042d066548b05 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 15 Jan 2020 13:02:11 +0100 Subject: [PATCH 328/843] fixes #88553 --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 91a1f9ddf9d..3786e910663 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -258,7 +258,20 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt e => (options.expandOnlyOnTwistieClick as ((e: T) => boolean))(e.element as T) ) ), - ariaProvider: undefined, + ariaProvider: options.ariaProvider && { + getPosInSet(el, index) { + return options.ariaProvider!.getPosInSet(el.element as T, index); + }, + getSetSize(el, index, listLength) { + return options.ariaProvider!.getSetSize(el.element as T, index, listLength); + }, + getRole: options.ariaProvider!.getRole ? (el) => { + return options.ariaProvider!.getRole!(el.element as T); + } : undefined, + isChecked: options.ariaProvider!.isChecked ? (e) => { + return options.ariaProvider?.isChecked!(e.element as T); + } : undefined + }, additionalScrollHeight: options.additionalScrollHeight }; } From 90024773cf531866dd13ced83f6ff568147d87f8 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 13:16:39 +0100 Subject: [PATCH 329/843] Fix #88672 --- src/vs/platform/configuration/common/configurationRegistry.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index bad49e688da..cc95a2666b7 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -301,9 +301,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { let properties = configuration.properties; if (properties) { for (let key in properties) { - let message; - if (validate && (message = validateProperty(key))) { - console.warn(message); + if (validate) { delete properties[key]; continue; } From 6cb0822d0c3bb91abd92c19fc41707ee911e5821 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jan 2020 13:56:33 +0100 Subject: [PATCH 330/843] log - ensure to convert any arg to string (main logger) --- src/vs/platform/log/common/log.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts index 88ce5fa9c31..cc548cfafc3 100644 --- a/src/vs/platform/log/common/log.ts +++ b/src/vs/platform/log/common/log.ts @@ -224,40 +224,48 @@ export class ConsoleLogInMainService extends AbstractLogService implements ILogS trace(message: string, ...args: any[]): void { if (this.getLevel() <= LogLevel.Trace) { - this.client.consoleLog('trace', [message, ...args]); + this.client.consoleLog('trace', [this.extractMessage(message), ...args]); } } debug(message: string, ...args: any[]): void { if (this.getLevel() <= LogLevel.Debug) { - this.client.consoleLog('debug', [message, ...args]); + this.client.consoleLog('debug', [this.extractMessage(message), ...args]); } } info(message: string, ...args: any[]): void { if (this.getLevel() <= LogLevel.Info) { - this.client.consoleLog('info', [message, ...args]); + this.client.consoleLog('info', [this.extractMessage(message), ...args]); } } warn(message: string | Error, ...args: any[]): void { if (this.getLevel() <= LogLevel.Warning) { - this.client.consoleLog('warn', [message, ...args]); + this.client.consoleLog('warn', [this.extractMessage(message), ...args]); } } error(message: string | Error, ...args: any[]): void { if (this.getLevel() <= LogLevel.Error) { - this.client.consoleLog('error', [toErrorMessage(message, this.getLevel() <= LogLevel.Trace), ...args]); + this.client.consoleLog('error', [this.extractMessage(message), ...args]); } } critical(message: string | Error, ...args: any[]): void { if (this.getLevel() <= LogLevel.Critical) { - this.client.consoleLog('critical', [toErrorMessage(message, this.getLevel() <= LogLevel.Trace), ...args]); + this.client.consoleLog('critical', [this.extractMessage(message), ...args]); } } + private extractMessage(msg: string | Error): string { + if (typeof msg === 'string') { + return msg; + } + + return toErrorMessage(msg, this.getLevel() <= LogLevel.Trace); + } + dispose(): void { // noop } From 58696e9629df48b6c63ef33f8ba14b503b14b333 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 13:56:57 +0100 Subject: [PATCH 331/843] Revert "Fix #88672" This reverts commit 90024773cf531866dd13ced83f6ff568147d87f8. --- src/vs/platform/configuration/common/configurationRegistry.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index cc95a2666b7..bad49e688da 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -301,7 +301,9 @@ class ConfigurationRegistry implements IConfigurationRegistry { let properties = configuration.properties; if (properties) { for (let key in properties) { - if (validate) { + let message; + if (validate && (message = validateProperty(key))) { + console.warn(message); delete properties[key]; continue; } From e7b0df32f4756643da423dd74b7ef837b6b9fa2e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 13:58:24 +0100 Subject: [PATCH 332/843] Fix #88672 --- src/vs/platform/configuration/common/configurationRegistry.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index bad49e688da..001d1a1712a 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -301,9 +301,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { let properties = configuration.properties; if (properties) { for (let key in properties) { - let message; - if (validate && (message = validateProperty(key))) { - console.warn(message); + if (validate && validateProperty(key)) { delete properties[key]; continue; } From 81ecff021b32d06da080d3d06cc829a1481351b4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jan 2020 15:48:08 +0100 Subject: [PATCH 333/843] make Action2 an abstract class so that ctor is clear and so that we can do things before/after run --- src/vs/platform/actions/common/actions.ts | 12 +- .../bulkEdit/browser/bulkEdit.contribution.ts | 116 +++++------ .../browser/extensions.contribution.ts | 80 ++++---- .../markers/browser/markers.contribution.ts | 185 ++++++++++-------- .../output/browser/output.contribution.ts | 48 ++--- .../electron-browser/remote.contribution.ts | 40 ++-- 6 files changed, 261 insertions(+), 220 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 7c036d9278c..c4fc2dd9826 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -348,20 +348,20 @@ export class SyncActionDescriptor { type OneOrN = T | T[]; -export interface IAction2Description extends ICommandAction { +export interface IAction2Options extends ICommandAction { f1?: boolean; menu?: OneOrN<{ id: MenuId } & Omit>; keybinding?: Omit; } -export interface IAction2 { - readonly desc: IAction2Description; - run(accessor: ServicesAccessor, ...args: any): any; +export abstract class Action2 { + constructor(readonly desc: Readonly) { } + abstract run(accessor: ServicesAccessor, ...args: any[]): any; } -export function registerAction2(action: IAction2): IDisposable { +export function registerAction2(ctor: { new(): Action2 }): IDisposable { const disposables = new DisposableStore(); - + const action = new ctor(); disposables.add(CommandsRegistry.registerCommand({ id: action.desc.id, handler: (accessor, ...args) => action.run(accessor, ...args), diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index 8e1ec18afc5..ec287b316d4 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -23,7 +23,7 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { URI } from 'vs/base/common/uri'; -import { MenuId, registerAction2, IAction2 } from 'vs/platform/actions/common/actions'; +import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IEditorInput } from 'vs/workbench/common/editor'; import type { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -112,28 +112,30 @@ class BulkEditPreviewContribution { // CMD: accept -registerAction2(new class ApplyAction implements IAction2 { +registerAction2(class ApplyAction extends Action2 { - readonly desc = { - id: 'refactorPreview.apply', - title: { value: localize('apply', "Apply Refactoring"), original: 'Apply Refactoring' }, - category: localize('cat', "Refactor Preview"), - icon: { id: 'codicon/check' }, - precondition: BulkEditPreviewContribution.ctxEnabled, - menu: [{ - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', BulkEditPane.ID), - group: 'navigation' - }, { - id: MenuId.BulkEditPaneContext, - order: 1 - }], - keybinding: { - weight: KeybindingWeight.WorkbenchContrib, - when: BulkEditPreviewContribution.ctxEnabled, - primary: KeyMod.Shift + KeyCode.Enter, - } - }; + constructor() { + super({ + id: 'refactorPreview.apply', + title: { value: localize('apply', "Apply Refactoring"), original: 'Apply Refactoring' }, + category: localize('cat', "Refactor Preview"), + icon: { id: 'codicon/check' }, + precondition: BulkEditPreviewContribution.ctxEnabled, + menu: [{ + id: MenuId.ViewTitle, + when: ContextKeyExpr.equals('view', BulkEditPane.ID), + group: 'navigation' + }, { + id: MenuId.BulkEditPaneContext, + order: 1 + }], + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + when: BulkEditPreviewContribution.ctxEnabled, + primary: KeyMod.Shift + KeyCode.Enter, + } + }); + } run(accessor: ServicesAccessor): any { const panelService = accessor.get(IPanelService); @@ -145,23 +147,25 @@ registerAction2(new class ApplyAction implements IAction2 { }); // CMD: discard -registerAction2(new class DiscardAction implements IAction2 { +registerAction2(class DiscardAction extends Action2 { - readonly desc = { - id: 'refactorPreview.discard', - title: { value: localize('Discard', "Discard Refactoring"), original: 'Discard Refactoring' }, - category: localize('cat', "Refactor Preview"), - icon: { id: 'codicon/clear-all' }, - precondition: BulkEditPreviewContribution.ctxEnabled, - menu: [{ - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', BulkEditPane.ID), - group: 'navigation' - }, { - id: MenuId.BulkEditPaneContext, - order: 2 - }] - }; + constructor() { + super({ + id: 'refactorPreview.discard', + title: { value: localize('Discard', "Discard Refactoring"), original: 'Discard Refactoring' }, + category: localize('cat', "Refactor Preview"), + icon: { id: 'codicon/clear-all' }, + precondition: BulkEditPreviewContribution.ctxEnabled, + menu: [{ + id: MenuId.ViewTitle, + when: ContextKeyExpr.equals('view', BulkEditPane.ID), + group: 'navigation' + }, { + id: MenuId.BulkEditPaneContext, + order: 2 + }] + }); + } run(accessor: ServicesAccessor): void | Promise { const panelService = accessor.get(IPanelService); @@ -174,24 +178,26 @@ registerAction2(new class DiscardAction implements IAction2 { // CMD: toggle -registerAction2(new class ToggleAction implements IAction2 { +registerAction2(class ToggleAction extends Action2 { - readonly desc = { - id: 'refactorPreview.toggleCheckedState', - title: { value: localize('toogleSelection', "Accept Change"), original: 'Accept Change' }, - category: localize('cat', "Refactor Preview"), - precondition: BulkEditPreviewContribution.ctxEnabled, - toggled: BulkEditPane.ctxChangeChecked, - keybinding: { - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: KeyCode.Space, - }, - menu: { - id: MenuId.BulkEditPaneContext, - group: 'navigation' - } - }; + constructor() { + super({ + id: 'refactorPreview.toggleCheckedState', + title: { value: localize('toogleSelection', "Accept Change"), original: 'Accept Change' }, + category: localize('cat', "Refactor Preview"), + precondition: BulkEditPreviewContribution.ctxEnabled, + toggled: BulkEditPane.ctxChangeChecked, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyCode.Space, + }, + menu: { + id: MenuId.BulkEditPaneContext, + group: 'navigation' + } + }); + } run(accessor: ServicesAccessor): void | Promise { const panelService = accessor.get(IPanelService); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 54100b78d50..f5fe47a7c09 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor, MenuRegistry, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionsLabel, ExtensionsChannelId, PreferencesLabel, IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -341,16 +341,20 @@ MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { // Extension Context Menu -registerAction2({ - desc: { - id: 'workbench.extensions.action.copyExtension', - title: { value: localize('workbench.extensions.action.copyExtension', "Copy"), original: 'Copy' }, - menu: { - id: MenuId.ExtensionContext, - group: '1_copy' - } - }, - async run(accessor, extensionId: string) { +registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.copyExtension', + title: { value: localize('workbench.extensions.action.copyExtension', "Copy"), original: 'Copy' }, + menu: { + id: MenuId.ExtensionContext, + group: '1_copy' + } + }); + } + + async run(accessor: ServicesAccessor, extensionId: string) { const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); let extension = extensionWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] || (await extensionWorkbenchService.queryGallery({ names: [extensionId], pageSize: 1 }, CancellationToken.None)).firstPage[0]; @@ -367,33 +371,41 @@ registerAction2({ } }); -registerAction2({ - desc: { - id: 'workbench.extensions.action.copyExtensionId', - title: { value: localize('workbench.extensions.action.copyExtensionId', "Copy Extension Id"), original: 'Copy Extension Id' }, - menu: { - id: MenuId.ExtensionContext, - group: '1_copy' - } - }, - async run(accessor, id: string) { +registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.copyExtensionId', + title: { value: localize('workbench.extensions.action.copyExtensionId', "Copy Extension Id"), original: 'Copy Extension Id' }, + menu: { + id: MenuId.ExtensionContext, + group: '1_copy' + } + }); + } + + async run(accessor: ServicesAccessor, id: string) { await accessor.get(IClipboardService).writeText(id); - }, + } }); -registerAction2({ - desc: { - id: 'workbench.extensions.action.configure', - title: { value: localize('workbench.extensions.action.configure', "Configure..."), original: 'Configure...' }, - menu: { - id: MenuId.ExtensionContext, - group: '2_configure', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasConfiguration')) - } - }, - async run(accessor, id: string) { +registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.configure', + title: { value: localize('workbench.extensions.action.configure', "Configure..."), original: 'Configure...' }, + menu: { + id: MenuId.ExtensionContext, + group: '2_configure', + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasConfiguration')) + } + }); + } + + async run(accessor: ServicesAccessor, id: string) { await accessor.get(IPreferencesService).openSettings(false, `@ext:${id}`); - }, + } }); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index 415fa4238ca..47bf7f1fff1 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -13,7 +13,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; import { Marker, RelatedInformation } from 'vs/workbench/contrib/markers/browser/markersModel'; import { MarkersView, getMarkersView } from 'vs/workbench/contrib/markers/browser/markersView'; -import { MenuId, MenuRegistry, SyncActionDescriptor, registerAction2 } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, SyncActionDescriptor, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { Registry } from 'vs/platform/registry/common/platform'; import { ShowProblemsPanelAction } from 'vs/workbench/contrib/markers/browser/markersViewActions'; @@ -33,6 +33,7 @@ import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExte import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import type { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; registerSingleton(IMarkersWorkbenchService, MarkersWorkbenchService, false); @@ -136,109 +137,123 @@ registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMarkersPanelA primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_M }), 'View: Toggle Problems (Errors, Warnings, Infos)', Messages.MARKERS_PANEL_VIEW_CATEGORY); registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowProblemsPanelAction, ShowProblemsPanelAction.ID, ShowProblemsPanelAction.LABEL), 'View: Focus Problems (Errors, Warnings, Infos)', Messages.MARKERS_PANEL_VIEW_CATEGORY); -registerAction2({ - desc: { - id: Constants.MARKER_COPY_ACTION_ID, - title: { value: localize('copyMarker', "Copy"), original: 'Copy' }, - menu: { - id: MenuId.ProblemsPanelContext, - when: Constants.MarkerFocusContextKey, - group: 'navigation' - }, - keybinding: { - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyMod.CtrlCmd | KeyCode.KEY_C, - when: Constants.MarkerFocusContextKey - }, - }, - async run(accessor) { +registerAction2(class extends Action2 { + constructor() { + super({ + id: Constants.MARKER_COPY_ACTION_ID, + title: { value: localize('copyMarker', "Copy"), original: 'Copy' }, + menu: { + id: MenuId.ProblemsPanelContext, + when: Constants.MarkerFocusContextKey, + group: 'navigation' + }, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + when: Constants.MarkerFocusContextKey + }, + }); + } + async run(accessor: ServicesAccessor) { await copyMarker(accessor.get(IPanelService), accessor.get(IClipboardService)); - }, + } }); -registerAction2({ - desc: { - id: Constants.MARKER_COPY_MESSAGE_ACTION_ID, - title: { value: localize('copyMessage', "Copy Message"), original: 'Copy Message' }, - menu: { - id: MenuId.ProblemsPanelContext, - when: Constants.MarkerFocusContextKey, - group: 'navigation' - }, - }, - async run(accessor) { +registerAction2(class extends Action2 { + constructor() { + super({ + id: Constants.MARKER_COPY_MESSAGE_ACTION_ID, + title: { value: localize('copyMessage', "Copy Message"), original: 'Copy Message' }, + menu: { + id: MenuId.ProblemsPanelContext, + when: Constants.MarkerFocusContextKey, + group: 'navigation' + }, + }); + } + async run(accessor: ServicesAccessor) { await copyMessage(accessor.get(IPanelService), accessor.get(IClipboardService)); - }, + } }); -registerAction2({ - desc: { - id: Constants.RELATED_INFORMATION_COPY_MESSAGE_ACTION_ID, - title: { value: localize('copyMessage', "Copy Message"), original: 'Copy Message' }, - menu: { - id: MenuId.ProblemsPanelContext, - when: Constants.RelatedInformationFocusContextKey, - group: 'navigation' - } - }, - async run(accessor) { +registerAction2(class extends Action2 { + constructor() { + super({ + id: Constants.RELATED_INFORMATION_COPY_MESSAGE_ACTION_ID, + title: { value: localize('copyMessage', "Copy Message"), original: 'Copy Message' }, + menu: { + id: MenuId.ProblemsPanelContext, + when: Constants.RelatedInformationFocusContextKey, + group: 'navigation' + } + }); + } + async run(accessor: ServicesAccessor) { await copyRelatedInformationMessage(accessor.get(IPanelService), accessor.get(IClipboardService)); } }); -registerAction2({ - desc: { - id: Constants.FOCUS_PROBLEMS_FROM_FILTER, - title: localize('focusProblemsList', "Focus problems view"), - keybinding: { - when: Constants.MarkerPanelFilterFocusContextKey, - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyMod.CtrlCmd | KeyCode.DownArrow - } - }, - run(accessor) { +registerAction2(class extends Action2 { + constructor() { + super({ + id: Constants.FOCUS_PROBLEMS_FROM_FILTER, + title: localize('focusProblemsList', "Focus problems view"), + keybinding: { + when: Constants.MarkerPanelFilterFocusContextKey, + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyCode.DownArrow + } + }); + } + run(accessor: ServicesAccessor) { focusProblemsView(accessor.get(IPanelService)); } }); -registerAction2({ - desc: { - id: Constants.MARKERS_PANEL_FOCUS_FILTER, - title: localize('focusProblemsFilter', "Focus problems filter"), - keybinding: { - when: Constants.MarkerPanelFocusContextKey, - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyMod.CtrlCmd | KeyCode.KEY_F - } - }, - run(accessor) { +registerAction2(class extends Action2 { + constructor() { + super({ + id: Constants.MARKERS_PANEL_FOCUS_FILTER, + title: localize('focusProblemsFilter', "Focus problems filter"), + keybinding: { + when: Constants.MarkerPanelFocusContextKey, + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyCode.KEY_F + } + }); + } + run(accessor: ServicesAccessor) { focusProblemsFilter(accessor.get(IPanelService)); } }); -registerAction2({ - desc: { - id: Constants.MARKERS_PANEL_SHOW_MULTILINE_MESSAGE, - title: { value: localize('show multiline', "Show message in multiple lines"), original: 'Problems: Show message in multiple lines' }, - category: localize('problems', "Problems"), - menu: { - id: MenuId.CommandPalette, - when: ActivePanelContext.isEqualTo(Constants.MARKERS_PANEL_ID) - } - }, - run(accessor) { +registerAction2(class extends Action2 { + constructor() { + super({ + id: Constants.MARKERS_PANEL_SHOW_MULTILINE_MESSAGE, + title: { value: localize('show multiline', "Show message in multiple lines"), original: 'Problems: Show message in multiple lines' }, + category: localize('problems', "Problems"), + menu: { + id: MenuId.CommandPalette, + when: ActivePanelContext.isEqualTo(Constants.MARKERS_PANEL_ID) + } + }); + } + run(accessor: ServicesAccessor) { const markersView = getMarkersView(accessor.get(IPanelService)); if (markersView) { markersView.markersViewModel.multiline = true; } } }); -registerAction2({ - desc: { - id: Constants.MARKERS_PANEL_SHOW_SINGLELINE_MESSAGE, - title: { value: localize('show singleline', "Show message in single line"), original: 'Problems: Show message in single line' }, - category: localize('problems', "Problems"), - menu: { - id: MenuId.CommandPalette, - when: ActivePanelContext.isEqualTo(Constants.MARKERS_PANEL_ID) - } - }, - run(accessor) { +registerAction2(class extends Action2 { + constructor() { + super({ + id: Constants.MARKERS_PANEL_SHOW_SINGLELINE_MESSAGE, + title: { value: localize('show singleline', "Show message in single line"), original: 'Problems: Show message in single line' }, + category: localize('problems', "Problems"), + menu: { + id: MenuId.CommandPalette, + when: ActivePanelContext.isEqualTo(Constants.MARKERS_PANEL_ID) + } + }); + } + run(accessor: ServicesAccessor) { const markersView = getMarkersView(accessor.get(IPanelService)); if (markersView) { markersView.markersViewModel.multiline = false; diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index 13a9fd16f1f..5bff7445b91 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { MenuId, MenuRegistry, SyncActionDescriptor, registerAction2 } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, SyncActionDescriptor, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { OutputService, LogContentProvider } from 'vs/workbench/contrib/output/browser/outputServices'; @@ -20,7 +20,7 @@ import { LogViewer, LogViewerInput } from 'vs/workbench/contrib/output/browser/l import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; // Register Service @@ -89,16 +89,18 @@ actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ShowLogsOutpu actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(OpenOutputLogFileAction, OpenOutputLogFileAction.ID, OpenOutputLogFileAction.LABEL), 'Developer: Open Log File...', devCategory); // Define clear command, contribute to editor context menu -registerAction2({ - desc: { - id: 'editor.action.clearoutput', - title: { value: nls.localize('clearOutput.label', "Clear Output"), original: 'Clear Output' }, - menu: { - id: MenuId.EditorContext, - when: CONTEXT_IN_OUTPUT - }, - }, - run(accessor) { +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'editor.action.clearoutput', + title: { value: nls.localize('clearOutput.label', "Clear Output"), original: 'Clear Output' }, + menu: { + id: MenuId.EditorContext, + when: CONTEXT_IN_OUTPUT + }, + }); + } + run(accessor: ServicesAccessor) { const activeChannel = accessor.get(IOutputService).getActiveChannel(); if (activeChannel) { activeChannel.clear(); @@ -106,16 +108,18 @@ registerAction2({ } }); -registerAction2({ - desc: { - id: 'workbench.action.openActiveLogOutputFile', - title: { value: nls.localize('openActiveLogOutputFile', "Open Active Log Output File"), original: 'Open Active Log Output File' }, - menu: { - id: MenuId.CommandPalette, - when: CONTEXT_ACTIVE_LOG_OUTPUT - }, - }, - run(accessor) { +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'workbench.action.openActiveLogOutputFile', + title: { value: nls.localize('openActiveLogOutputFile', "Open Active Log Output File"), original: 'Open Active Log Output File' }, + menu: { + id: MenuId.CommandPalette, + when: CONTEXT_ACTIVE_LOG_OUTPUT + }, + }); + } + run(accessor: ServicesAccessor) { accessor.get(IInstantiationService).createInstance(OpenLogOutputFile).run(); } }); diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index 965821ee7bd..69209fe8950 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -12,7 +12,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; +import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchContributionsExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar'; @@ -70,29 +70,33 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc this._register(this.windowCommandMenu); const category = nls.localize('remote.category', "Remote"); - - registerAction2({ - desc: { - id: WINDOW_ACTIONS_COMMAND_ID, - category, - title: { value: nls.localize('remote.showMenu', "Show Remote Menu"), original: 'Show Remote Menu' }, - f1: true, - }, - run: () => this.showIndicatorActions(this.windowCommandMenu) + const that = this; + registerAction2(class extends Action2 { + constructor() { + super({ + id: WINDOW_ACTIONS_COMMAND_ID, + category, + title: { value: nls.localize('remote.showMenu', "Show Remote Menu"), original: 'Show Remote Menu' }, + f1: true, + }); + } + run = () => that.showIndicatorActions(that.windowCommandMenu); }); this.remoteAuthority = environmentService.configuration.remoteAuthority; Deprecated_RemoteAuthorityContext.bindTo(this.contextKeyService).set(this.remoteAuthority || ''); if (this.remoteAuthority) { - registerAction2({ - desc: { - id: CLOSE_REMOTE_COMMAND_ID, - category, - title: { value: nls.localize('remote.close', "Close Remote Connection"), original: 'Close Remote Connection' }, - f1: true - }, - run: () => this.remoteAuthority && hostService.openWindow({ forceReuseWindow: true }) + registerAction2(class extends Action2 { + constructor() { + super({ + id: CLOSE_REMOTE_COMMAND_ID, + category, + title: { value: nls.localize('remote.close', "Close Remote Connection"), original: 'Close Remote Connection' }, + f1: true + }); + } + run = () => that.remoteAuthority && hostService.openWindow({ forceReuseWindow: true }); }); // Pending entry until extensions are ready From 090ff120d4128b3ba2cad0b488152be80075ba42 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jan 2020 15:58:57 +0100 Subject: [PATCH 334/843] simplify toggle command --- .../bulkEdit/browser/bulkEdit.contribution.ts | 3 +-- .../contrib/bulkEdit/browser/bulkEditPane.ts | 14 ++------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index ec287b316d4..f5871255ae4 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -183,10 +183,9 @@ registerAction2(class ToggleAction extends Action2 { constructor() { super({ id: 'refactorPreview.toggleCheckedState', - title: { value: localize('toogleSelection', "Accept Change"), original: 'Accept Change' }, + title: { value: localize('toogleSelection', "Toggle Change"), original: 'Toggle Change' }, category: localize('cat', "Refactor Preview"), precondition: BulkEditPreviewContribution.ctxEnabled, - toggled: BulkEditPane.ctxChangeChecked, keybinding: { weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 53513f38d55..9af6f4022d1 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -22,7 +22,7 @@ import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; @@ -41,15 +41,11 @@ const enum State { export class BulkEditPane extends ViewPane { static readonly ID = 'refactorPreview'; - static readonly ctxChangeChecked = new RawContextKey('refactorPreview.changeChecked', true); private _tree!: WorkbenchAsyncDataTree; private _message!: HTMLSpanElement; private readonly _disposables = new DisposableStore(); - - private readonly _ctxElementChecked: IContextKey; - private readonly _sessionDisposables = new DisposableStore(); private _currentResolve?: (edit?: WorkspaceEdit) => void; private _currentInput?: BulkFileOperations; @@ -74,7 +70,6 @@ export class BulkEditPane extends ViewPane { ); this.element.classList.add('bulk-edit-panel', 'show-file-icons'); - this._ctxElementChecked = BulkEditPane.ctxChangeChecked.bindTo(_contextKeyService); } dispose(): void { @@ -120,11 +115,6 @@ export class BulkEditPane extends ViewPane { } })); - this._disposables.add(this._tree.onDidChangeFocus(e => { - const [first] = e.elements; - this._ctxElementChecked.set(first && first.edit.isChecked()); - })); - this._disposables.add(this._tree.onContextMenu(this._onContextMenu, this)); // message @@ -276,7 +266,7 @@ export class BulkEditPane extends ViewPane { this._contextMenuService.showContextMenu({ getActions: () => actions, - getAnchor: () => e.anchor!, + getAnchor: () => e.anchor, onHide: () => { disposable.dispose(); menu.dispose(); From d54ac9ffa19fec4885aebdb1a80a316f8e9adcd5 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jan 2020 15:55:03 +0100 Subject: [PATCH 335/843] #85216 reduce options for first time sync dialog --- .../userDataSync/browser/userDataSync.ts | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 6291311c69d..22304eeca47 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -35,7 +35,7 @@ import { IOutputService } from 'vs/workbench/contrib/output/common/output'; import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; import { IAuthenticationService, ChangeAccountEventData } from 'vs/workbench/services/authentication/browser/authenticationService'; import { Account } from 'vs/editor/common/modes'; -import { canceled, isPromiseCanceledError } from 'vs/base/common/errors'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; const enum MSAAuthStatus { Initializing = 'Initializing', @@ -415,26 +415,15 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo const hasPreviouslySynced = await this.userDataSyncService.hasPreviouslySynced(); if (hasRemote && !hasPreviouslySynced) { - const result = await this.dialogService.show( - Severity.Info, - localize('firs time sync', "First time Sync"), - [ - localize('continue', "Continue"), - localize('cancel', "Cancel"), - localize('download', "Download"), - localize('upload', "Upload"), - ], - { - cancelId: 1, - detail: localize('first time sync detail', "Synchronising from this device for the first time. Would you like to \nDownload and replace with the data from cloud or \nUpload and replace with the data from this device?") - } - ); + const result = await this.dialogService.confirm({ + type: 'info', + message: localize('firs time sync', "First time Sync"), + primaryButton: localize('download', "Download"), + detail: localize('first time sync detail', "Would you like to download and replace with the data from cloud?"), + }); - switch (result.choice) { - case 1: return Promise.reject(canceled()); - case 2: return this.userDataSyncService.pull(); - // case 3: return this.userDataSyncService.push(); - case 3: return this.notificationService.info('Upload: Not yet supported'); + if (result.confirmed) { + await this.userDataSyncService.pull(); } } } From 4560e524b8db758a0bcc0daddbac9bc1631bc7e1 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 15 Jan 2020 16:15:43 +0100 Subject: [PATCH 336/843] Fixes #84897: Ranges and positions inside graphemes are considered valid (needed for editing) --- src/vs/editor/common/model/textModel.ts | 138 ++++++++++-------- .../test/browser/controller/cursor.test.ts | 34 +++++ .../test/common/model/textModel.test.ts | 81 ---------- 3 files changed, 110 insertions(+), 143 deletions(-) diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 9bc6dc21a62..95f98b75f49 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -158,6 +158,17 @@ class TextModelSnapshot implements model.ITextSnapshot { const invalidFunc = () => { throw new Error(`Invalid change accessor`); }; +const enum StringOffsetValidationType { + /** + * Even allowed in surrogate pairs + */ + Relaxed = 0, + /** + * Not allowed in surrogate pairs + */ + SurrogatePairs = 1, +} + export class TextModel extends Disposable implements model.ITextModel { private static readonly MODEL_SYNC_LIMIT = 50 * 1024 * 1024; // 50 MB @@ -673,7 +684,7 @@ export class TextModel extends Disposable implements model.ITextModel { public getOffsetAt(rawPosition: IPosition): number { this._assertNotDisposed(); - let position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, false); + let position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, StringOffsetValidationType.Relaxed); return this._buffer.getOffsetAt(position.lineNumber, position.column); } @@ -868,10 +879,7 @@ export class TextModel extends Disposable implements model.ITextModel { return new Range(startLineNumber, startColumn, endLineNumber, endColumn); } - /** - * @param strict Do NOT allow a position inside a high-low surrogate pair - */ - private _isValidPosition(lineNumber: number, column: number, strict: boolean): boolean { + private _isValidPosition(lineNumber: number, column: number, validationType: StringOffsetValidationType): boolean { if (typeof lineNumber !== 'number' || typeof column !== 'number') { return false; } @@ -893,14 +901,19 @@ export class TextModel extends Disposable implements model.ITextModel { return false; } + if (column === 1) { + return true; + } + const maxColumn = this.getLineMaxColumn(lineNumber); if (column > maxColumn) { return false; } - if (strict) { - const [charStartOffset,] = strings.getCharContainingOffset(this._buffer.getLineContent(lineNumber), column - 1); - if (column !== charStartOffset + 1) { + if (validationType === StringOffsetValidationType.SurrogatePairs) { + // !!At this point, column > 1 + const charCodeBefore = this._buffer.getLineCharCode(lineNumber, column - 2); + if (strings.isHighSurrogate(charCodeBefore)) { return false; } } @@ -908,10 +921,7 @@ export class TextModel extends Disposable implements model.ITextModel { return true; } - /** - * @param strict Do NOT allow a position inside a high-low surrogate pair - */ - private _validatePosition(_lineNumber: number, _column: number, strict: boolean): Position { + private _validatePosition(_lineNumber: number, _column: number, validationType: StringOffsetValidationType): Position { const lineNumber = Math.floor((typeof _lineNumber === 'number' && !isNaN(_lineNumber)) ? _lineNumber : 1); const column = Math.floor((typeof _column === 'number' && !isNaN(_column)) ? _column : 1); const lineCount = this._buffer.getLineCount(); @@ -933,10 +943,13 @@ export class TextModel extends Disposable implements model.ITextModel { return new Position(lineNumber, maxColumn); } - if (strict) { - const [charStartOffset,] = strings.getCharContainingOffset(this._buffer.getLineContent(lineNumber), column - 1); - if (column !== charStartOffset + 1) { - return new Position(lineNumber, charStartOffset + 1); + if (validationType === StringOffsetValidationType.SurrogatePairs) { + // If the position would end up in the middle of a high-low surrogate pair, + // we move it to before the pair + // !!At this point, column > 1 + const charCodeBefore = this._buffer.getLineCharCode(lineNumber, column - 2); + if (strings.isHighSurrogate(charCodeBefore)) { + return new Position(lineNumber, column - 1); } } @@ -944,94 +957,95 @@ export class TextModel extends Disposable implements model.ITextModel { } public validatePosition(position: IPosition): Position { + const validationType = StringOffsetValidationType.SurrogatePairs; this._assertNotDisposed(); // Avoid object allocation and cover most likely case if (position instanceof Position) { - if (this._isValidPosition(position.lineNumber, position.column, true)) { + if (this._isValidPosition(position.lineNumber, position.column, validationType)) { return position; } } - return this._validatePosition(position.lineNumber, position.column, true); + return this._validatePosition(position.lineNumber, position.column, validationType); } - /** - * @param strict Do NOT allow a range to have its boundaries inside a high-low surrogate pair - */ - private _isValidRange(range: Range, strict: boolean): boolean { + private _isValidRange(range: Range, validationType: StringOffsetValidationType): boolean { const startLineNumber = range.startLineNumber; const startColumn = range.startColumn; const endLineNumber = range.endLineNumber; const endColumn = range.endColumn; - if (!this._isValidPosition(startLineNumber, startColumn, false)) { + if (!this._isValidPosition(startLineNumber, startColumn, StringOffsetValidationType.Relaxed)) { return false; } - if (!this._isValidPosition(endLineNumber, endColumn, false)) { + if (!this._isValidPosition(endLineNumber, endColumn, StringOffsetValidationType.Relaxed)) { return false; } - if (strict) { - const startLineContent = this._buffer.getLineContent(startLineNumber); - if (startColumn < startLineContent.length + 1) { - const [charStartOffset,] = strings.getCharContainingOffset(startLineContent, startColumn - 1); - if (startColumn !== charStartOffset + 1) { - return false; - } - } + if (validationType === StringOffsetValidationType.SurrogatePairs) { + const charCodeBeforeStart = (startColumn > 1 ? this._buffer.getLineCharCode(startLineNumber, startColumn - 2) : 0); + const charCodeBeforeEnd = (endColumn > 1 && endColumn <= this._buffer.getLineLength(endLineNumber) ? this._buffer.getLineCharCode(endLineNumber, endColumn - 2) : 0); - if (endColumn >= 2) { - const endLineContent = (endLineNumber === startLineNumber ? startLineContent : this._buffer.getLineContent(endLineNumber)); - const [, charEndOffset] = strings.getCharContainingOffset(endLineContent, endColumn - 2); - if (endColumn !== charEndOffset + 1) { - return false; - } - } + const startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart); + const endInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeEnd); - return true; + if (!startInsideSurrogatePair && !endInsideSurrogatePair) { + return true; + } + return false; } return true; } public validateRange(_range: IRange): Range { + const validationType = StringOffsetValidationType.SurrogatePairs; this._assertNotDisposed(); // Avoid object allocation and cover most likely case if ((_range instanceof Range) && !(_range instanceof Selection)) { - if (this._isValidRange(_range, true)) { + if (this._isValidRange(_range, validationType)) { return _range; } } - const start = this._validatePosition(_range.startLineNumber, _range.startColumn, false); - const end = this._validatePosition(_range.endLineNumber, _range.endColumn, false); + const start = this._validatePosition(_range.startLineNumber, _range.startColumn, StringOffsetValidationType.Relaxed); + const end = this._validatePosition(_range.endLineNumber, _range.endColumn, StringOffsetValidationType.Relaxed); const startLineNumber = start.lineNumber; - let startColumn = start.column; + const startColumn = start.column; const endLineNumber = end.lineNumber; - let endColumn = end.column; - const isEmpty = (startLineNumber === endLineNumber && startColumn === endColumn); + const endColumn = end.column; - const startLineContent = this._buffer.getLineContent(startLineNumber); - if (startColumn < startLineContent.length + 1) { - const [charStartOffset,] = strings.getCharContainingOffset(startLineContent, startColumn - 1); - if (startColumn !== charStartOffset + 1) { - if (isEmpty) { - // do not expand a collapsed range, simply move it to a valid location - return new Range(startLineNumber, charStartOffset + 1, startLineNumber, charStartOffset + 1); - } - startColumn = charStartOffset + 1; - } - } + if (validationType === StringOffsetValidationType.SurrogatePairs) { + const charCodeBeforeStart = (startColumn > 1 ? this._buffer.getLineCharCode(startLineNumber, startColumn - 2) : 0); + const charCodeBeforeEnd = (endColumn > 1 && endColumn <= this._buffer.getLineLength(endLineNumber) ? this._buffer.getLineCharCode(endLineNumber, endColumn - 2) : 0); - if (endColumn >= 2) { - const endLineContent = (endLineNumber === startLineNumber ? startLineContent : this._buffer.getLineContent(endLineNumber)); - const [, charEndOffset] = strings.getCharContainingOffset(endLineContent, endColumn - 2); - if (endColumn !== charEndOffset + 1) { - endColumn = charEndOffset + 1; + const startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart); + const endInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeEnd); + + if (!startInsideSurrogatePair && !endInsideSurrogatePair) { + return new Range(startLineNumber, startColumn, endLineNumber, endColumn); } + + if (startLineNumber === endLineNumber && startColumn === endColumn) { + // do not expand a collapsed range, simply move it to a valid location + return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn - 1); + } + + if (startInsideSurrogatePair && endInsideSurrogatePair) { + // expand range at both ends + return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn + 1); + } + + if (startInsideSurrogatePair) { + // only expand range at the start + return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn); + } + + // only expand range at the end + return new Range(startLineNumber, startColumn, endLineNumber, endColumn + 1); } return new Range(startLineNumber, startColumn, endLineNumber, endColumn); diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 0f300688e23..4d490f7d2c1 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -2303,6 +2303,40 @@ suite('Editor Controller - Regression tests', () => { model.dispose(); }); + + test('issue #84897: Left delete behavior in some languages is changed', () => { + let model = createTextModel( + [ + 'สวัสดี' + ].join('\n') + ); + + withTestCodeEditor(null, { model: model }, (editor, cursor) => { + editor.setSelections([ + new Selection(1, 7, 1, 7) + ]); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), 'สวัสด'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), 'สวัส'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), 'สวั'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), 'สว'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), 'ส'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getValue(EndOfLinePreference.LF), ''); + }); + + model.dispose(); + }); }); suite('Editor Controller - Cursor Configuration', () => { diff --git a/src/vs/editor/test/common/model/textModel.test.ts b/src/vs/editor/test/common/model/textModel.test.ts index ed74fa6cca2..0771acc14ae 100644 --- a/src/vs/editor/test/common/model/textModel.test.ts +++ b/src/vs/editor/test/common/model/textModel.test.ts @@ -743,87 +743,6 @@ suite('Editor Model - TextModel', () => { assert.deepEqual(m.validatePosition(new Position(2, 1.5)), new Position(2, 1), 'h'); }); - function assertValidatePosition(m: TextModel, lineNumber: number, column: number, expectedColumn: number): void { - const input = new Position(lineNumber, column); - const actual = m.validatePosition(input); - const expected = new Position(lineNumber, expectedColumn); - assert.deepEqual(actual, expected, `validatePosition for ${input}, got ${actual}, expected ${expected}`); - } - - function assertValidateRange(m: TextModel, input: Range, expected: Range): void { - const actual = m.validateRange(input); - assert.deepEqual(actual, expected, `validateRange for ${input}, got ${actual}, expected ${expected}`); - } - - test('grapheme breaking', () => { - const m = TextModel.createFromString([ - 'abcabc', - 'ãããããã', - '辻󠄀辻󠄀辻󠄀', - 'புபுபு', - ].join('\n')); - - assertValidatePosition(m, 2, 1, 1); - assertValidatePosition(m, 2, 2, 1); - assertValidatePosition(m, 2, 3, 3); - assertValidatePosition(m, 2, 4, 3); - assertValidatePosition(m, 2, 5, 5); - assertValidatePosition(m, 2, 6, 5); - assertValidatePosition(m, 2, 7, 7); - assertValidatePosition(m, 2, 8, 7); - assertValidatePosition(m, 2, 9, 9); - assertValidatePosition(m, 2, 10, 9); - assertValidatePosition(m, 2, 11, 11); - assertValidatePosition(m, 2, 12, 11); - assertValidatePosition(m, 2, 13, 13); - assertValidatePosition(m, 2, 14, 13); - - assertValidatePosition(m, 3, 1, 1); - assertValidatePosition(m, 3, 2, 1); - assertValidatePosition(m, 3, 3, 1); - assertValidatePosition(m, 3, 4, 4); - assertValidatePosition(m, 3, 5, 4); - assertValidatePosition(m, 3, 6, 4); - assertValidatePosition(m, 3, 7, 7); - assertValidatePosition(m, 3, 8, 7); - assertValidatePosition(m, 3, 9, 7); - assertValidatePosition(m, 3, 10, 10); - - assertValidatePosition(m, 4, 1, 1); - assertValidatePosition(m, 4, 2, 1); - assertValidatePosition(m, 4, 3, 3); - assertValidatePosition(m, 4, 4, 3); - assertValidatePosition(m, 4, 5, 5); - assertValidatePosition(m, 4, 6, 5); - assertValidatePosition(m, 4, 7, 7); - - assertValidateRange(m, new Range(2, 1, 2, 1), new Range(2, 1, 2, 1)); - assertValidateRange(m, new Range(2, 1, 2, 2), new Range(2, 1, 2, 3)); - assertValidateRange(m, new Range(2, 1, 2, 3), new Range(2, 1, 2, 3)); - assertValidateRange(m, new Range(2, 1, 2, 4), new Range(2, 1, 2, 5)); - assertValidateRange(m, new Range(2, 1, 2, 5), new Range(2, 1, 2, 5)); - assertValidateRange(m, new Range(2, 2, 2, 2), new Range(2, 1, 2, 1)); - assertValidateRange(m, new Range(2, 2, 2, 3), new Range(2, 1, 2, 3)); - assertValidateRange(m, new Range(2, 2, 2, 4), new Range(2, 1, 2, 5)); - assertValidateRange(m, new Range(2, 2, 2, 5), new Range(2, 1, 2, 5)); - - assertValidateRange(m, new Range(3, 1, 3, 1), new Range(3, 1, 3, 1)); - assertValidateRange(m, new Range(3, 1, 3, 2), new Range(3, 1, 3, 4)); - assertValidateRange(m, new Range(3, 1, 3, 3), new Range(3, 1, 3, 4)); - assertValidateRange(m, new Range(3, 1, 3, 4), new Range(3, 1, 3, 4)); - assertValidateRange(m, new Range(3, 1, 3, 5), new Range(3, 1, 3, 7)); - assertValidateRange(m, new Range(3, 1, 3, 6), new Range(3, 1, 3, 7)); - assertValidateRange(m, new Range(3, 1, 3, 7), new Range(3, 1, 3, 7)); - assertValidateRange(m, new Range(3, 2, 3, 2), new Range(3, 1, 3, 1)); - assertValidateRange(m, new Range(3, 2, 3, 3), new Range(3, 1, 3, 4)); - assertValidateRange(m, new Range(3, 2, 3, 4), new Range(3, 1, 3, 4)); - assertValidateRange(m, new Range(3, 2, 3, 5), new Range(3, 1, 3, 7)); - assertValidateRange(m, new Range(3, 2, 3, 6), new Range(3, 1, 3, 7)); - assertValidateRange(m, new Range(3, 2, 3, 7), new Range(3, 1, 3, 7)); - - m.dispose(); - }); - test('issue #71480: validateRange handle floats', () => { let m = TextModel.createFromString('line one\nline two'); From b76f607039611f5f4eeaef18d7f1db76460bcb16 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 15 Jan 2020 16:19:16 +0100 Subject: [PATCH 337/843] fixes #88675 --- .../files/electron-browser/fileActions.contribution.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts index fc370f6d577..b8bce9e57ec 100644 --- a/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts @@ -27,7 +27,7 @@ import { ShowOpenedFileInNewWindow } from 'vs/workbench/contrib/files/browser/fi import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; const REVEAL_IN_OS_COMMAND_ID = 'revealFileInOS'; -const REVEAL_IN_OS_LABEL = isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder"); +const REVEAL_IN_OS_LABEL = isWindows ? nls.localize('revealInWindows', "Reveal in File Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder"); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: REVEAL_IN_OS_COMMAND_ID, @@ -63,7 +63,7 @@ appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, Re const revealInOsCommand = { id: REVEAL_IN_OS_COMMAND_ID, - title: isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder") + title: isWindows ? nls.localize('revealInWindows', "Reveal in File Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder") }; MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: 'navigation', @@ -84,7 +84,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { // Command Palette const category = { value: nls.localize('filesCategory', "File"), original: 'File' }; -appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category); +appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in File Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category); const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowOpenedFileInNewWindow, ShowOpenedFileInNewWindow.ID, ShowOpenedFileInNewWindow.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_O) }), 'File: Open Active File in New Window', category.value); From 6d1f663dd74c6ae412d3ca9e40153707b4f63996 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jan 2020 16:36:03 +0100 Subject: [PATCH 338/843] integration tests - enable logging when running against build --- scripts/test-integration.bat | 5 +++++ scripts/test-integration.sh | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index 946cf383914..695f1c940fe 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -23,6 +23,11 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( call yarn gulp compile-extension:html-language-features-server call yarn gulp compile-extension:json-language-features-server + :: Configuration for more verbose output + set VSCODE_CLI=1 + set ELECTRON_ENABLE_LOGGING=1 + set ELECTRON_ENABLE_STACK_DUMPING=1 + echo "Running integration tests with '%INTEGRATION_TEST_ELECTRON_PATH%' as build." ) diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index 21652b632cf..89851e48908 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -30,6 +30,11 @@ else yarn gulp compile-extension:html-language-features-server yarn gulp compile-extension:json-language-features-server + # Configuration for more verbose output + export VSCODE_CLI=1 + export ELECTRON_ENABLE_STACK_DUMPING=1 + export ELECTRON_ENABLE_LOGGING=1 + echo "Running integration tests with '$INTEGRATION_TEST_ELECTRON_PATH' as build." fi @@ -42,7 +47,6 @@ fi "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR - mkdir -p $ROOT/extensions/emmet/test-fixtures "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR rm -rf $ROOT/extensions/emmet/test-fixtures From 3b51c87d58062ab5d486df41c516435298c69bf6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jan 2020 16:38:42 +0100 Subject: [PATCH 339/843] :up: distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 71baf2eb073..c0cdd440cd1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.42.0", - "distro": "683b7a48a0cab9871ae34d129cc31e1036746b37", + "distro": "3171dfd018976ad5d8248fb4e3b712c439788be9", "author": { "name": "Microsoft Corporation" }, From ceec95bb1623522c13d550c9ca53232692194922 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jan 2020 16:35:44 +0100 Subject: [PATCH 340/843] menu title menu configurable and optional --- src/vs/platform/actions/common/actions.ts | 3 ++- .../browser/parts/views/customView.ts | 2 +- .../browser/parts/views/viewPaneContainer.ts | 26 +++++++++++-------- .../bulkEdit/browser/bulkEdit.contribution.ts | 14 +++++----- .../contrib/bulkEdit/browser/bulkEditPane.ts | 4 +-- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index c4fc2dd9826..f654a5b9a90 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -108,7 +108,8 @@ export const enum MenuId { CommentThreadActions, CommentTitle, CommentActions, - BulkEditPaneContext, + BulkEditTitle, + BulkEditContext, } export interface IMenuActionOptions { diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index ceefbf34af6..4bd86842a7d 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -56,7 +56,7 @@ export class CustomTreeViewPane extends ViewPane { @IContextKeyService contextKeyService: IContextKeyService, @IInstantiationService instantiationService: IInstantiationService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title, titleMenuId: MenuId.ViewTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); const { treeView } = (Registry.as(Extensions.ViewsRegistry).getView(options.id)); this.treeView = treeView; this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this)); diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 822718b18f3..7730d4b4b7c 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -51,6 +51,7 @@ export interface IViewPaneOptions extends IPaneOptions { id: string; title: string; showActionsAlways?: boolean; + titleMenuId?: MenuId; } export abstract class ViewPane extends Pane implements IView { @@ -75,7 +76,7 @@ export abstract class ViewPane extends Pane implements IView { readonly id: string; title: string; - private readonly menuActions: CustomViewMenuActions; + private readonly menuActions?: ViewMenuActions; protected actionRunner?: IActionRunner; protected toolbar?: ToolBar; @@ -100,8 +101,10 @@ export abstract class ViewPane extends Pane implements IView { this.showActionsAlways = !!options.showActionsAlways; this.focusedViewContextKey = FocusedViewContext.bindTo(contextKeyService); - this.menuActions = this._register(instantiationService.createInstance(CustomViewMenuActions, this.id)); - this._register(this.menuActions.onDidChangeTitle(() => this.updateActions())); + if (options.titleMenuId !== undefined) { + this.menuActions = this._register(instantiationService.createInstance(ViewMenuActions, this.id, options.titleMenuId)); + this._register(this.menuActions.onDidChangeTitle(() => this.updateActions())); + } } setVisible(visible: boolean): void { @@ -215,11 +218,11 @@ export abstract class ViewPane extends Pane implements IView { } getActions(): IAction[] { - return this.menuActions.getPrimaryActions(); + return this.menuActions ? this.menuActions.getPrimaryActions() : []; } getSecondaryActions(): IAction[] { - return this.menuActions.getSecondaryActions(); + return this.menuActions ? this.menuActions.getSecondaryActions() : []; } getActionViewItem(action: IAction): IActionViewItem | undefined { @@ -770,7 +773,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { } -class CustomViewMenuActions extends Disposable { +class ViewMenuActions extends Disposable { private primaryActions: IAction[] = []; private readonly titleActionsDisposable = this._register(new MutableDisposable()); @@ -780,24 +783,25 @@ class CustomViewMenuActions extends Disposable { readonly onDidChangeTitle: Event = this._onDidChangeTitle.event; constructor( - id: string, + viewId: string, + menuId: MenuId, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IMenuService private readonly menuService: IMenuService, ) { super(); const scopedContextKeyService = this._register(this.contextKeyService.createScoped()); - scopedContextKeyService.createKey('view', id); + scopedContextKeyService.createKey('view', viewId); - const titleMenu = this._register(this.menuService.createMenu(MenuId.ViewTitle, scopedContextKeyService)); + const menu = this._register(this.menuService.createMenu(menuId, scopedContextKeyService)); const updateActions = () => { this.primaryActions = []; this.secondaryActions = []; - this.titleActionsDisposable.value = createAndFillInActionBarActions(titleMenu, undefined, { primary: this.primaryActions, secondary: this.secondaryActions }); + this.titleActionsDisposable.value = createAndFillInActionBarActions(menu, undefined, { primary: this.primaryActions, secondary: this.secondaryActions }); this._onDidChangeTitle.fire(); }; - this._register(titleMenu.onDidChange(updateActions)); + this._register(menu.onDidChange(updateActions)); updateActions(); this._register(toDisposable(() => { diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index f5871255ae4..14c03711095 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -14,7 +14,7 @@ import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewCon import { localize } from 'vs/nls'; import { ViewPaneContainer, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { PaneCompositePanel } from 'vs/workbench/browser/panel'; -import { RawContextKey, IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { BulkEditPreviewProvider } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditPreview'; @@ -122,11 +122,10 @@ registerAction2(class ApplyAction extends Action2 { icon: { id: 'codicon/check' }, precondition: BulkEditPreviewContribution.ctxEnabled, menu: [{ - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', BulkEditPane.ID), + id: MenuId.BulkEditTitle, group: 'navigation' }, { - id: MenuId.BulkEditPaneContext, + id: MenuId.BulkEditContext, order: 1 }], keybinding: { @@ -157,11 +156,10 @@ registerAction2(class DiscardAction extends Action2 { icon: { id: 'codicon/clear-all' }, precondition: BulkEditPreviewContribution.ctxEnabled, menu: [{ - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', BulkEditPane.ID), + id: MenuId.BulkEditTitle, group: 'navigation' }, { - id: MenuId.BulkEditPaneContext, + id: MenuId.BulkEditContext, order: 2 }] }); @@ -192,7 +190,7 @@ registerAction2(class ToggleAction extends Action2 { primary: KeyCode.Space, }, menu: { - id: MenuId.BulkEditPaneContext, + id: MenuId.BulkEditContext, group: 'navigation' } }); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 9af6f4022d1..dc6147415e2 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -65,7 +65,7 @@ export class BulkEditPane extends ViewPane { @IConfigurationService configurationService: IConfigurationService, ) { super( - options, + { ...options, titleMenuId: MenuId.BulkEditTitle }, keybindingService, contextMenuService, configurationService, _contextKeyService, _instaService ); @@ -260,7 +260,7 @@ export class BulkEditPane extends ViewPane { } private _onContextMenu(e: ITreeContextMenuEvent): void { - const menu = this._menuService.createMenu(MenuId.BulkEditPaneContext, this._contextKeyService); + const menu = this._menuService.createMenu(MenuId.BulkEditContext, this._contextKeyService); const actions: IAction[] = []; const disposable = createAndFillInContextMenuActions(menu, undefined, actions, this._contextMenuService); From 11350bdda3fc19d2d822971b9fba7871e3904e7d Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 15 Jan 2020 07:42:32 -0800 Subject: [PATCH 341/843] Fix bug causing search editor to not be replaced with saved when dirty. --- src/vs/workbench/contrib/search/browser/searchEditorCommands.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts index 38be7b88471..e01d2e2cb0b 100644 --- a/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts +++ b/src/vs/workbench/contrib/search/browser/searchEditorCommands.ts @@ -167,6 +167,7 @@ export class SearchEditorInput extends EditorInput { const path = await this.promptForPath(this.resource, this.suggestFileName()); if (path) { if (await this.textFileService.saveAs(this.resource, path, options)) { + this.setDirty(false); if (options?.context !== SaveContext.EDITOR_CLOSE && !isEqual(path, this.resource)) { const replacement = this.instantiationService.createInstance(SearchEditorInput, this.config, undefined, path); await this.editorService.replaceEditors([{ editor: this, replacement, options: { pinned: true } }], group); From 1be0f0b9347f2797d7569988626c1e6fa9886042 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jan 2020 17:07:40 +0100 Subject: [PATCH 342/843] bulk: return early if edit does nothing --- src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 11136917170..3a0d834deb7 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -389,6 +389,10 @@ export class BulkEditService implements IBulkEditService { async apply(edit: WorkspaceEdit, options?: IBulkEditOptions): Promise { + if (edit.edits.length === 0) { + return { ariaSummary: localize('nothing', "Made no edits") }; + } + if (this._previewHandler && options?.showPreview) { edit = await this._previewHandler(edit, options); } From e7bdeaddc857b9d0ecb2a611ffda0835da8c6889 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jan 2020 17:35:25 +0100 Subject: [PATCH 343/843] fix breadcrumbs model mem leak --- src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index a3065e23ba9..2090614dace 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -71,6 +71,7 @@ export class EditorBreadcrumbsModel { dispose(): void { this._cfgFilePath.dispose(); this._cfgSymbolPath.dispose(); + this._outlineDisposables.dispose(); this._disposables.dispose(); } From 4db305c825f3c08f9ff59193bfce49c94a051d86 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 15 Jan 2020 17:36:50 +0100 Subject: [PATCH 344/843] Add semantic tokens to inspectTMScopes, rename to inspectEditorTokens --- .../browser/codeEditor.contribution.ts | 2 +- .../inspectEditorTokens.css} | 14 +- .../inspectEditorTokens.ts} | 200 ++++++++++++------ 3 files changed, 149 insertions(+), 67 deletions(-) rename src/vs/workbench/contrib/codeEditor/browser/{inspectTMScopes/inspectTMScopes.css => inspectEditorTokens/inspectEditorTokens.css} (82%) rename src/vs/workbench/contrib/codeEditor/browser/{inspectTMScopes/inspectTMScopes.ts => inspectEditorTokens/inspectEditorTokens.ts} (56%) diff --git a/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts b/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts index 740449c991f..dc8c179721c 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts @@ -8,7 +8,7 @@ import './accessibility/accessibility'; import './diffEditorHelper'; import './inspectKeybindings'; import './largeFileOptimizations'; -import './inspectTMScopes/inspectTMScopes'; +import './inspectEditorTokens/inspectEditorTokens'; import './toggleMinimap'; import './toggleMultiCursorModifier'; import './toggleRenderControlCharacter'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.css b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.css similarity index 82% rename from src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.css rename to src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.css index a28c0560783..9c5d9ac63de 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.css +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.css @@ -3,37 +3,37 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.tm-inspect-widget { +.token-inspect-widget { z-index: 50; user-select: text; -webkit-user-select: text; padding: 10px; } -.tm-token { +.tiw-token { font-family: var(--monaco-monospace-font); } -.tm-metadata-separator { +.tiw-metadata-separator { height: 1px; border: 0; } -.tm-token-length { +.tiw-token-length { font-weight: normal; font-size: 60%; float: right; } -.tm-metadata-table { +.tiw-metadata-table { width: 100%; } -.tm-metadata-value { +.tiw-metadata-value { font-family: var(--monaco-monospace-font); text-align: right; } -.tm-theme-selector { +.tiw-theme-selector { font-family: var(--monaco-monospace-font); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts similarity index 56% rename from src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts rename to src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts index c2a701f247d..372fe1a5296 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./inspectTMScopes'; +import 'vs/css!./inspectEditorTokens'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { CharCode } from 'vs/base/common/charCode'; @@ -16,7 +16,7 @@ import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorCon import { Position } from 'vs/editor/common/core/position'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { FontStyle, LanguageIdentifier, StandardTokenType, TokenMetadata, TokenizationRegistry } from 'vs/editor/common/modes'; +import { FontStyle, LanguageIdentifier, StandardTokenType, TokenMetadata, SemanticTokensProviderRegistry, SemanticTokensLegend, SemanticTokens } from 'vs/editor/common/modes'; import { IModeService } from 'vs/editor/common/services/modeService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry'; @@ -24,13 +24,14 @@ import { HIGH_CONTRAST, registerThemingParticipant } from 'vs/platform/theme/com import { findMatchingThemeRule } from 'vs/workbench/services/textMate/common/TMHelper'; import { ITextMateService, IGrammar, IToken, StackElement } from 'vs/workbench/services/textMate/common/textMateService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; -class InspectTMScopesController extends Disposable implements IEditorContribution { +class InspectEditorTokensController extends Disposable implements IEditorContribution { - public static readonly ID = 'editor.contrib.inspectTMScopes'; + public static readonly ID = 'editor.contrib.inspectEditorTokens'; - public static get(editor: ICodeEditor): InspectTMScopesController { - return editor.getContribution(InspectTMScopesController.ID); + public static get(editor: ICodeEditor): InspectEditorTokensController { + return editor.getContribution(InspectEditorTokensController.ID); } private _editor: ICodeEditor; @@ -38,7 +39,7 @@ class InspectTMScopesController extends Disposable implements IEditorContributio private _themeService: IWorkbenchThemeService; private _modeService: IModeService; private _notificationService: INotificationService; - private _widget: InspectTMScopesWidget | null; + private _widget: InspectEditorTokensWidget | null; constructor( editor: ICodeEditor, @@ -72,7 +73,7 @@ class InspectTMScopesController extends Disposable implements IEditorContributio if (!this._editor.hasModel()) { return; } - this._widget = new InspectTMScopesWidget(this._editor, this._textMateService, this._modeService, this._themeService, this._notificationService); + this._widget = new InspectEditorTokensWidget(this._editor, this._textMateService, this._modeService, this._themeService, this._notificationService); } public stop(): void { @@ -91,19 +92,19 @@ class InspectTMScopesController extends Disposable implements IEditorContributio } } -class InspectTMScopes extends EditorAction { +class InspectEditorTokens extends EditorAction { constructor() { super({ - id: 'editor.action.inspectTMScopes', - label: nls.localize('inspectTMScopes', "Developer: Inspect TM Scopes"), - alias: 'Developer: Inspect TM Scopes', + id: 'editor.action.inspectEditorTokens', + label: nls.localize('inspectEditorTokens', "Developer: Inspect Editor Tokens"), + alias: 'Developer: Inspect Editor Tokens', precondition: undefined }); } public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - let controller = InspectTMScopesController.get(editor); + let controller = InspectEditorTokensController.get(editor); if (controller) { controller.toggle(); } @@ -120,9 +121,9 @@ interface ICompleteLineTokenization { interface IDecodedMetadata { languageIdentifier: LanguageIdentifier; tokenType: StandardTokenType; - fontStyle: FontStyle; - foreground: Color; - background: Color; + fontStyle: string; + foreground?: string; + background?: string; } function renderTokenText(tokenText: string): string { @@ -160,9 +161,11 @@ function renderTokenText(tokenText: string): string { return result; } -class InspectTMScopesWidget extends Disposable implements IContentWidget { +type SemanticTokensResult = { tokens: SemanticTokens, legend: SemanticTokensLegend }; - private static readonly _ID = 'editor.contrib.inspectTMScopesWidget'; +class InspectEditorTokensWidget extends Disposable implements IContentWidget { + + private static readonly _ID = 'editor.contrib.inspectEditorTokensWidget'; // Editor.IContentWidget.allowEditorOverflow public readonly allowEditorOverflow = true; @@ -175,6 +178,8 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget { private readonly _model: ITextModel; private readonly _domNode: HTMLElement; private readonly _grammar: Promise; + private readonly _semanticTokens: Promise; + private readonly _currentRequestCancellationTokenSource: CancellationTokenSource; constructor( editor: IActiveCodeEditor, @@ -191,8 +196,10 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget { this._notificationService = notificationService; this._model = this._editor.getModel(); this._domNode = document.createElement('div'); - this._domNode.className = 'tm-inspect-widget'; + this._domNode.className = 'token-inspect-widget'; + this._currentRequestCancellationTokenSource = new CancellationTokenSource(); this._grammar = textMateService.createGrammar(this._model.getLanguageIdentifier().language); + this._semanticTokens = this._computeSemanticTokens(); this._beginCompute(this._editor.getPosition()); this._register(this._editor.onDidChangeCursorPosition((e) => this._beginCompute(this._editor.getPosition()))); this._editor.addContentWidget(this); @@ -201,33 +208,34 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget { public dispose(): void { this._isDisposed = true; this._editor.removeContentWidget(this); + this._currentRequestCancellationTokenSource.cancel(); super.dispose(); } public getId(): string { - return InspectTMScopesWidget._ID; + return InspectEditorTokensWidget._ID; } private _beginCompute(position: Position): void { dom.clearNode(this._domNode); this._domNode.appendChild(document.createTextNode(nls.localize('inspectTMScopesWidget.loading', "Loading..."))); - this._grammar.then( - (grammar) => { - if (!grammar) { - throw new Error(`Could not find grammar for language!`); - } - this._compute(grammar, position); - }, - (err) => { - this._notificationService.warn(err); - setTimeout(() => { - InspectTMScopesController.get(this._editor).stop(); - }); + + Promise.all([this._grammar, this._semanticTokens]).then(([grammar, semanticTokens]) => { + if (!grammar) { + throw new Error(`Could not find grammar for language!`); } - ); + this._compute(grammar, semanticTokens, position); + }, (err) => { + this._notificationService.warn(err); + + setTimeout(() => { + InspectEditorTokensController.get(this._editor).stop(); + }); + }); + } - private _compute(grammar: IGrammar, position: Position): void { + private _compute(grammar: IGrammar, semanticTokens: SemanticTokensResult | null, position: Position): void { if (this._isDisposed) { return; } @@ -250,53 +258,89 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget { } } + let semanticMetadata: IDecodedMetadata | undefined = undefined; + let semanticClasssification; + if (semanticTokens) { + semanticClasssification = this._getSemanticTokenAtPosition(semanticTokens, position); + if (semanticClasssification) { + const metadata = this._themeService.getTheme().getTokenStyleMetadata(semanticClasssification.type, semanticClasssification.modifiers); + if (metadata) { + semanticMetadata = this._decodeMetadata(metadata); + } + } + } + + let result = ''; let tokenStartIndex = data.tokens1[token1Index].startIndex; let tokenEndIndex = data.tokens1[token1Index].endIndex; let tokenText = this._model.getLineContent(position.lineNumber).substring(tokenStartIndex, tokenEndIndex); - result += `

${renderTokenText(tokenText)}(${tokenText.length} ${tokenText.length === 1 ? 'char' : 'chars'})

`; + result += `

${renderTokenText(tokenText)}(${tokenText.length} ${tokenText.length === 1 ? 'char' : 'chars'})

`; - result += ``; + result += ``; let metadata = this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]); - result += ``; - result += ``; - result += ``; - result += ``; - result += ``; - result += ``; - if (metadata.background.isOpaque()) { - result += ``; - } else { - result += ''; + result += ``; + result += ``; + result += ``; + if (semanticClasssification) { + result += ``; + + const modifiers = semanticClasssification.modifiers.join(' ') || '-'; + result += ``; } result += ``; + result += `