diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index ad2d028465e..2eb4b6432b3 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -8,7 +8,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"August 2020\"", + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"September 2020\"", "editable": true }, { diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index d1452276f6e..a5de65e0bd1 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -8,7 +8,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks\n\n// current milestone name\n$milestone=milestone:\"August 2020\"", + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks\n\n// current milestone name\n$milestone=milestone:\"September 2020\"", "editable": true }, { diff --git a/.vscode/notebooks/verification.github-issues b/.vscode/notebooks/verification.github-issues index 79f7f8a5c0e..4e65452ea56 100644 --- a/.vscode/notebooks/verification.github-issues +++ b/.vscode/notebooks/verification.github-issues @@ -14,7 +14,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks \n$milestone=milestone:\"July 2020\"", + "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks \n$milestone=milestone:\"September 2020\"", "editable": true }, { diff --git a/.vscode/searches/TrustedTypes.code-search b/.vscode/searches/TrustedTypes.code-search new file mode 100644 index 00000000000..2112eb183c8 --- /dev/null +++ b/.vscode/searches/TrustedTypes.code-search @@ -0,0 +1,483 @@ +# Query: .innerHTML = +# Flags: CaseSensitive WordMatch +# Including: src/vs/**/*.{t,j}s +# Excluding: *.test.ts +# ContextLines: 3 + +57 results - 39 files + +src/vs/base/browser/markdownRenderer.ts: + 156 const strValue = values[0]; + 157 const span = element.querySelector(`div[data-code="${id}"]`); + 158 if (span) { + 159: span.innerHTML = strValue; + 160 } + 161 }).catch(err => { + 162 // ignore + + 238 return true; + 239 } + 240 + 241: element.innerHTML = insane(renderedMarkdown, { + 242 allowedSchemes, + 243 // allowedTags should included everything that markdown renders to. + 244 // Since we have our own sanitize function for marked, it's possible we missed some tag so let insane make sure. + +src/vs/base/browser/ui/codicons/codiconStyles.ts: + 20 for (let c of iconRegistry.all) { + 21 rules.push(formatRule(c)); + 22 } + 23: codiconStyleSheet.innerHTML = rules.join('\n'); + 24 } + 25 + 26 const delayer = new RunOnceScheduler(updateAll, 0); + +src/vs/base/browser/ui/contextview/contextview.ts: + 157 this.shadowRootHostElement = DOM.$('.shadow-root-host'); + 158 this.container.appendChild(this.shadowRootHostElement); + 159 this.shadowRoot = this.shadowRootHostElement.attachShadow({ mode: 'open' }); + 160: this.shadowRoot.innerHTML = ` + 161 + +src/vs/base/browser/ui/list/listWidget.ts: + 826 + 827 const newStyles = content.join('\n'); + 828 if (newStyles !== this.styleElement.innerHTML) { + 829: this.styleElement.innerHTML = newStyles; + 830 } + 831 } + 832 } + +src/vs/base/browser/ui/menu/menu.ts: + 229 private initializeStyleSheet(container: HTMLElement): void { + 230 if (isInShadowDOM(container)) { + 231 this.styleSheet = createStyleSheet(container); + 232: this.styleSheet.innerHTML = MENU_WIDGET_CSS; + 233 } else { + 234 if (!Menu.globalStyleSheet) { + 235 Menu.globalStyleSheet = createStyleSheet(); + 236: Menu.globalStyleSheet.innerHTML = MENU_WIDGET_CSS; + 237 } + 238 + 239 this.styleSheet = Menu.globalStyleSheet; + +src/vs/base/browser/ui/menu/menubar.ts: + 548 } + 549 + 550 innerHtml = innerHtml.replace(/&&/g, '&'); + 551: titleElement.innerHTML = innerHtml; + 552 } else { + 553: titleElement.innerHTML = cleanMenuLabel.replace(/&&/g, '&'); + 554 } + 555 + 556 let mnemonicMatches = MENU_MNEMONIC_REGEX.exec(label); + +src/vs/base/browser/ui/sash/sash.ts: + 266 } + 267 } + 268 + 269: style.innerHTML = `* { cursor: ${cursor} !important; }`; + 270 }; + 271 + 272 const disposables = new DisposableStore(); + +src/vs/base/browser/ui/selectBox/selectBoxCustom.ts: + 367 content.push(`.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row.option-disabled:hover { outline: none !important; }`); + 368 } + 369 + 370: this.styleElement.innerHTML = content.join('\n'); + 371 + 372 this.applyStyles(); + 373 } + + 705 }); + 706 + 707 + 708: container.innerHTML = this.options[longest].text + (!!this.options[longest].decoratorRight ? (this.options[longest].decoratorRight + ' ') : ''); + 709 elementWidth = dom.getTotalWidth(container); + 710 } + 711 + +src/vs/base/browser/ui/tree/abstractTree.ts: + 1476 + 1477 const newStyles = content.join('\n'); + 1478 if (newStyles !== this.styleElement.innerHTML) { + 1479: this.styleElement.innerHTML = newStyles; + 1480 } + 1481 + 1482 this.view.style(styles); + +src/vs/base/parts/quickinput/browser/quickInput.ts: + 1693 } + 1694 const newStyles = content.join('\n'); + 1695 if (newStyles !== this.ui.styleSheet.innerHTML) { + 1696: this.ui.styleSheet.innerHTML = newStyles; + 1697 } + 1698 } + 1699 } + +src/vs/code/electron-browser/workbench/workbench.js: + 124 const style = document.createElement('style'); + 125 style.className = 'initialShellColors'; + 126 document.head.appendChild(style); + 127: style.innerHTML = `body { background-color: ${shellBackground}; color: ${shellForeground}; margin: 0; padding: 0; }`; + 128 + 129 if (data && data.layoutInfo) { + 130 // restore parts if possible (we might not always store layout info) + + 150 + 151 if (configuration.folderUri || configuration.workspace) { + 152 // folder or workspace -> status bar color, sidebar + 153: splash.innerHTML = ` + 154
+ 155
+ 156
+ + 158 `; + 159 } else { + 160 // empty -> speical status bar color, no sidebar + 161: splash.innerHTML = ` + 162
+ 163
+ 164
+ +src/vs/code/electron-sandbox/issue/issueReporterMain.ts: + 57 const platformClass = platform.isWindows ? 'windows' : platform.isLinux ? 'linux' : 'mac'; + 58 addClass(document.body, platformClass); // used by our fonts + 59 + 60: document.body.innerHTML = BaseHtml(); + 61 const issueReporter = new IssueReporter(configuration); + 62 issueReporter.render(); + 63 document.body.style.display = 'block'; + + 244 content.push(`.monaco-text-button:not(.disabled):hover, .monaco-text-button:focus { background-color: ${styles.buttonHoverBackground} !important; }`); + 245 } + 246 + 247: styleTag.innerHTML = content.join('\n'); + 248 document.head.appendChild(styleTag); + 249 document.body.style.color = styles.color || ''; + 250 } + +src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts: + 282 this.listeners.clear(); + 283 + 284 const tableHead = document.createElement('thead'); + 285: tableHead.innerHTML = ` + 286 ${localize('cpu', "CPU %")} + 287 ${localize('memory', "Memory (MB)")} + 288 ${localize('pid', "PID")} + + 319 content.push(`.highest { color: ${styles.highlightForeground}; }`); + 320 } + 321 + 322: styleTag.innerHTML = content.join('\n'); + 323 if (document.head) { + 324 document.head.appendChild(styleTag); + 325 } + +src/vs/editor/browser/view/domLineBreaksComputer.ts: + 107 allCharOffsets[i] = tmp[0]; + 108 allVisibleColumns[i] = tmp[1]; + 109 } + 110: containerDomNode.innerHTML = sb.build(); + 111 + 112 containerDomNode.style.position = 'absolute'; + 113 containerDomNode.style.top = '10000'; + +src/vs/editor/browser/view/viewLayer.ts: + 507 private _finishRenderingNewLines(ctx: IRendererContext, domNodeIsEmpty: boolean, newLinesHTML: string, wasNew: boolean[]): void { + 508 const lastChild = this.domNode.lastChild; + 509 if (domNodeIsEmpty || !lastChild) { + 510: this.domNode.innerHTML = newLinesHTML; + 511 } else { + 512 lastChild.insertAdjacentHTML('afterend', newLinesHTML); + 513 } + + 525 private _finishRenderingInvalidLines(ctx: IRendererContext, invalidLinesHTML: string, wasInvalid: boolean[]): void { + 526 const hugeDomNode = document.createElement('div'); + 527 + 528: hugeDomNode.innerHTML = invalidLinesHTML; + 529 + 530 for (let i = 0; i < ctx.linesLength; i++) { + 531 const line = ctx.lines[i]; + +src/vs/editor/browser/widget/diffEditorWidget.ts: + 2157 + 2158 let domNode = document.createElement('div'); + 2159 domNode.className = `view-lines line-delete ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`; + 2160: domNode.innerHTML = sb.build(); + 2161 Configuration.applyFontInfoSlow(domNode, fontInfo); + 2162 + 2163 let marginDomNode = document.createElement('div'); + 2164 marginDomNode.className = 'inline-deleted-margin-view-zone'; + 2165: marginDomNode.innerHTML = marginHTML.join(''); + 2166 Configuration.applyFontInfoSlow(marginDomNode, fontInfo); + 2167 + 2168 return { + +src/vs/editor/standalone/browser/colorizer.ts: + 40 let text = domNode.firstChild ? domNode.firstChild.nodeValue : ''; + 41 domNode.className += ' ' + theme; + 42 let render = (str: string) => { + 43: domNode.innerHTML = str; + 44 }; + 45 return this.colorize(modeService, text || '', mimeType, options).then(render, (err) => console.error(err)); + 46 } + +src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts: + 211 if (!this._globalStyleElement) { + 212 this._globalStyleElement = dom.createStyleSheet(); + 213 this._globalStyleElement.className = 'monaco-colors'; + 214: this._globalStyleElement.innerHTML = this._css; + 215 this._styleElements.push(this._globalStyleElement); + 216 } + 217 return Disposable.None; + + 220 private _registerShadowDomContainer(domNode: HTMLElement): IDisposable { + 221 const styleElement = dom.createStyleSheet(domNode); + 222 styleElement.className = 'monaco-colors'; + 223: styleElement.innerHTML = this._css; + 224 this._styleElements.push(styleElement); + 225 return { + 226 dispose: () => { + + 290 ruleCollector.addRule(generateTokensCSSForColorMap(colorMap)); + 291 + 292 this._css = cssRules.join('\n'); + 293: this._styleElements.forEach(styleElement => styleElement.innerHTML = this._css); + 294 + 295 TokenizationRegistry.setColorMap(colorMap); + 296 this._onColorThemeChange.fire(theme); + +src/vs/editor/test/browser/controller/imeTester.ts: + 55 let content = this._model.getModelLineContent(i); + 56 r += content + '
'; + 57 } + 58: output.innerHTML = r; + 59 } + 60 } + 61 + + 69 let title = document.createElement('div'); + 70 title.className = 'title'; + 71 + 72: title.innerHTML = description + '. Type ' + inputStr + ''; + 73 container.appendChild(title); + 74 + 75 let startBtn = document.createElement('button'); + +src/vs/workbench/browser/parts/compositePart.ts: + 414 const $this = this; + 415 return { + 416 updateTitle: (id, title, keybinding) => { + 417: titleLabel.innerHTML = strings.escape(title); + 418 titleLabel.title = keybinding ? nls.localize('titleTooltip', "{0} ({1})", title, keybinding) : title; + 419 }, + 420 + +src/vs/workbench/browser/parts/statusbar/statusbarPart.ts: + 672 this.styleElement = createStyleSheet(container); + 673 } + 674 + 675: this.styleElement.innerHTML = `.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor}; }`; + 676 } + 677 + 678 private doCreateStatusItem(id: string, alignment: StatusbarAlignment, ...extraClasses: string[]): HTMLElement { + +src/vs/workbench/contrib/comments/browser/commentsView.ts: + 109 content.push(`.comments-panel .comments-panel-container .text code { color: ${codeTextForegroundColor}; }`); + 110 } + 111 + 112: styleElement.innerHTML = content.join('\n'); + 113 } + 114 + 115 private async renderComments(): Promise { + +src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts: + 692 } + 693 + 694 if (label) { + 695: this._headingLabel.innerHTML = strings.escape(label); + 696 this._headingLabel.setAttribute('aria-label', label); + 697 } + 698 } + + 916 font-weight: ${fontInfo.fontWeight}; + 917 }`); + 918 + 919: this._styleElement.innerHTML = content.join('\n'); + 920 + 921 // Editor decorations should also be responsive to theme changes + 922 this.setCommentEditorDecorations(); + +src/vs/workbench/contrib/debug/browser/repl.ts: + 306 const replInputLineHeight = this.replInput.getOption(EditorOption.lineHeight); + 307 + 308 // Set the font size, font family, line height and align the twistie to be centered, and input theme color + 309: this.styleElement.innerHTML = ` + 310 .repl .repl-tree .expression { + 311 font-size: ${fontSize}px; + 312 font-family: ${fontFamily}; + +src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts: + 84 this.styleElement = createStyleSheet(container); + 85 } + 86 + 87: this.styleElement.innerHTML = `.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor} !important; }`; + 88 } + 89 + 90 private getColorKey(noFolderColor: string, debuggingColor: string, normalColor: string): string { + +src/vs/workbench/contrib/files/browser/views/explorerView.ts: + 812 + 813 const newStyles = content.join('\n'); + 814 if (newStyles !== this.styleElement.innerHTML) { + 815: this.styleElement.innerHTML = newStyles; + 816 } + 817 } + 818 + +src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts: + 222 + 223 const newStyles = content.join('\n'); + 224 if (newStyles !== this.styleElement.innerHTML) { + 225: this.styleElement.innerHTML = newStyles; + 226 } + 227 } + 228 } + +src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts: + 1033 + 1034 const newStyles = content.join('\n'); + 1035 if (newStyles !== this.styleElement.innerHTML) { + 1036: this.styleElement.innerHTML = newStyles; + 1037 } + 1038 } + 1039 + +src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts: + 443 + 444 private getMarkdownDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { + 445 const dragImageContainer = DOM.$('.cell-drag-image.monaco-list-row.focused.markdown-cell-row'); + 446: dragImageContainer.innerHTML = templateData.container.outerHTML; + 447 + 448 // Remove all rendered content nodes after the + 449 const markdownContent = dragImageContainer.querySelector('.cell.markdown')!; + + 587 + 588 private getDragImageImpl(templateData: BaseCellRenderTemplate, editor: ICodeEditor, type: 'code' | 'markdown'): HTMLElement | null { + 589 const dragImageContainer = DOM.$(`.cell-drag-image.monaco-list-row.focused.${type}-cell-row`); + 590: dragImageContainer.innerHTML = templateData.container.innerHTML; + 591 + 592 const editorContainer = dragImageContainer.querySelector('.cell-editor-container'); + 593 if (!editorContainer) { + + 599 return null; + 600 } + 601 + 602: editorContainer.innerHTML = richEditorText; + 603 + 604 return dragImageContainer; + 605 } + +src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts: + 375 addMouseoverListeners(outputNode, outputId); + 376 const content = data.content; + 377 if (content.type === RenderOutputType.Html) { + 378: outputNode.innerHTML = content.htmlContent; + 379 cellOutputContainer.appendChild(outputNode); + 380 domEval(outputNode); + 381 } else { + +src/vs/workbench/contrib/sash/browser/sash.ts: + 35 const size = clamp(this.configurationService.getValue(this.configurationName) ?? minSize, minSize, maxSize); + 36 + 37 // Update styles + 38: this.stylesheet.innerHTML = ` + 39 .monaco-sash.vertical { cursor: ew-resize; top: 0; width: ${size}px; height: 100%; } + 40 .monaco-sash.horizontal { cursor: ns-resize; left: 0; width: 100%; height: ${size}px; } + 41 .monaco-sash:not(.disabled).orthogonal-start::before, .monaco-sash:not(.disabled).orthogonal-end::after { content: ' '; height: ${size * 2}px; width: ${size * 2}px; z-index: 100; display: block; cursor: all-scroll; position: absolute; } + +src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts: + 1301 + 1302 private setViewState(state: IViewState): void { + 1303 this.viewState = state; + 1304: this.stylesheet.innerHTML = ` + 1305 .monaco-editor .dirty-diff-modified,.monaco-editor .dirty-diff-added{border-left-width:${state.width}px;} + 1306 .monaco-editor .dirty-diff-modified, .monaco-editor .dirty-diff-added, .monaco-editor .dirty-diff-deleted { + 1307 opacity: ${state.visibility === 'always' ? 1 : 0}; + +src/vs/workbench/contrib/watermark/browser/watermark.ts: + 141 const dd = dom.append(dl, $('dd')); + 142 const keybinding = new KeybindingLabel(dd, OS, { renderUnboundKeybindings: true }); + 143 keybinding.set(this.keybindingService.lookupKeybinding(entry.id)); + 144: dd.innerHTML = keybinding.element.outerHTML; + 145 }); + 146 }; + 147 + +src/vs/workbench/contrib/webview/browser/webviewIconManager.ts: + 61 } + 62 } + 63 } + 64: this._styleElement.innerHTML = cssRules.join('\n'); + 65 } + 66 } + +src/vs/workbench/contrib/webview/browser/pre/main.js: + 386 // apply default styles + 387 const defaultStyles = newDocument.createElement('style'); + 388 defaultStyles.id = '_defaultStyles'; + 389: defaultStyles.innerHTML = defaultCssRules; + 390 newDocument.head.prepend(defaultStyles); + 391 + 392 applyStyles(newDocument, newDocument.body); + +src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts: + 328 + 329 const prodName = container.querySelector('.welcomePage .title .caption') as HTMLElement; + 330 if (prodName) { + 331: prodName.innerHTML = this.productService.nameLong; + 332 } + 333 + 334 recentlyOpened.then(({ workspaces }) => { + +src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts: + 281 + 282 const content = model.main.textEditorModel.getValue(EndOfLinePreference.LF); + 283 if (!strings.endsWith(input.resource.path, '.md')) { + 284: this.content.innerHTML = content; + 285 this.updateSizeClasses(); + 286 this.decorateContent(); + 287 this.contentDisposables.push(this.keybindingService.onDidUpdateKeybindings(() => this.decorateContent())); + + 303 const innerContent = document.createElement('div'); + 304 innerContent.classList.add('walkThroughContent'); // only for markdown files + 305 const markdown = this.expandMacros(content); + 306: innerContent.innerHTML = marked(markdown, { renderer }); + 307 this.content.appendChild(innerContent); + 308 + 309 model.snippets.forEach((snippet, i) => { + +src/vs/workbench/services/textMate/browser/abstractTextMateService.ts: + 312 grammarFactory.setTheme(theme, tokenColorMap); + 313 let colorMap = AbstractTextMateService._toColorMap(tokenColorMap); + 314 let cssRules = generateTokensCSSForColorMap(colorMap); + 315: this._styleElement.innerHTML = cssRules; + 316 TokenizationRegistry.setColorMap(colorMap); + 317 } + 318 + +src/vs/workbench/services/themes/browser/workbenchThemeService.ts: + 692 const elStyle = document.createElement('style'); + 693 elStyle.type = 'text/css'; + 694 elStyle.className = rulesClassName; + 695: elStyle.innerHTML = styleSheetContent; + 696 document.head.appendChild(elStyle); + 697 } else { + 698: (themeStyles[0]).innerHTML = styleSheetContent; + 699 } + 700 } + 701 diff --git a/.yarnrc b/.yarnrc index 3c6eccfb102..fbf612d0989 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "9.2.1" +target "9.3.0" runtime "electron" diff --git a/build/lib/electron.js b/build/lib/electron.js index 3c7a876d607..301a7695060 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -41,7 +41,7 @@ exports.config = { darwinBundleDocumentType(["bowerrc"], 'resources/darwin/bower.icns'), darwinBundleDocumentType(["c", "h"], 'resources/darwin/c.icns'), darwinBundleDocumentType(["config", "editorconfig", "gitattributes", "gitconfig", "gitignore", "ini"], 'resources/darwin/config.icns'), - darwinBundleDocumentType(["cc", "cpp", "cxx", "hh", "hpp", "hxx"], 'resources/darwin/cpp.icns'), + darwinBundleDocumentType(["cc", "cpp", "cxx", "c++", "hh", "hpp", "hxx", "h++"], 'resources/darwin/cpp.icns'), darwinBundleDocumentType(["cs", "csx"], 'resources/darwin/csharp.icns'), darwinBundleDocumentType(["css"], 'resources/darwin/css.icns'), darwinBundleDocumentType(["go"], 'resources/darwin/go.icns'), diff --git a/build/lib/electron.ts b/build/lib/electron.ts index e969aa8ecba..125759c5815 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -46,7 +46,7 @@ export const config = { darwinBundleDocumentType(["bowerrc"], 'resources/darwin/bower.icns'), darwinBundleDocumentType(["c", "h"], 'resources/darwin/c.icns'), darwinBundleDocumentType(["config", "editorconfig", "gitattributes", "gitconfig", "gitignore", "ini"], 'resources/darwin/config.icns'), - darwinBundleDocumentType(["cc", "cpp", "cxx", "hh", "hpp", "hxx"], 'resources/darwin/cpp.icns'), + darwinBundleDocumentType(["cc", "cpp", "cxx", "c++", "hh", "hpp", "hxx", "h++"], 'resources/darwin/cpp.icns'), darwinBundleDocumentType(["cs", "csx"], 'resources/darwin/csharp.icns'), darwinBundleDocumentType(["css"], 'resources/darwin/css.icns'), darwinBundleDocumentType(["go"], 'resources/darwin/go.icns'), diff --git a/build/win32/code.iss b/build/win32/code.iss index 6908759b7aa..7f4b36e71aa 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -173,6 +173,13 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bower Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\bower.ico"; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.c++\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.c++\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.c++"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c++"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c++"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c++\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c++\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles + Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.c\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.c\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.c"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C}"; Flags: uninsdeletekey; Tasks: associatewithfiles @@ -442,6 +449,13 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.h++\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.h++\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.h++"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h++"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++ Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h++"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h++\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h++\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles + Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hh\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hh\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.hh"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++ Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles diff --git a/cgmanifest.json b/cgmanifest.json index e6e8ce8a5c1..859af568b1b 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "03c7a54dc534ce1867d4393b9b1a6989d4a7e005" + "commitHash": "fb03807cd21915ddc3aa2521ba4f5ba14597bd7e" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "9.2.1" + "version": "9.3.0" }, { "component": { diff --git a/extensions/bat/language-configuration.json b/extensions/bat/language-configuration.json index 17bc92f6a91..e205fb5f5c4 100644 --- a/extensions/bat/language-configuration.json +++ b/extensions/bat/language-configuration.json @@ -1,6 +1,6 @@ { "comments": { - "lineComment": "REM" + "lineComment": "@REM" }, "brackets": [ ["{", "}"], @@ -22,8 +22,8 @@ ], "folding": { "markers": { - "start": "^\\s*(::\\s*|REM\\s+)#region", - "end": "^\\s*(::\\s*|REM\\s+)#endregion" + "start": "^\\s*(::|REM|@REM)\\s*#region", + "end": "^\\s*(::|REM|@REM)\\s*#endregion" } } } diff --git a/extensions/configuration-editing/schemas/attachContainer.schema.json b/extensions/configuration-editing/schemas/attachContainer.schema.json index fc53bb896f1..2b7952446f0 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -50,6 +50,7 @@ "type": "string", "enum": [ "none", + "loginShell", "loginInteractiveShell", "interactiveShell" ], diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index 999a50e7788..b37e07fa65c 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -96,6 +96,7 @@ "type": "string", "enum": [ "none", + "loginShell", "loginInteractiveShell", "interactiveShell" ], diff --git a/extensions/cpp/package.json b/extensions/cpp/package.json index d302aa46ffd..1c8a244d285 100644 --- a/extensions/cpp/package.json +++ b/extensions/cpp/package.json @@ -34,8 +34,8 @@ ".c++", ".hpp", ".hh", - ".hxx", - ".h++", + ".hxx", + ".h++", ".h", ".ii", ".ino", diff --git a/extensions/debug-server-ready/src/extension.ts b/extensions/debug-server-ready/src/extension.ts index 91e996d665b..9f8737f307f 100644 --- a/extensions/debug-server-ready/src/extension.ts +++ b/extensions/debug-server-ready/src/extension.ts @@ -146,18 +146,13 @@ class ServerReadyDetector extends vscode.Disposable { break; case 'debugWithChrome': - if (vscode.env.remoteName === 'wsl' || !!vscode.extensions.getExtension('msjsdiag.debugger-for-chrome')) { - vscode.debug.startDebugging(session.workspaceFolder, { - type: 'chrome', - name: 'Chrome Debug', - request: 'launch', - url: uri, - webRoot: args.webRoot || WEB_ROOT - }, session); - } else { - const errMsg = localize('server.ready.chrome.not.installed', "The action '{0}' requires the '{1}' extension.", 'debugWithChrome', 'Debugger for Chrome'); - vscode.window.showErrorMessage(errMsg, { modal: true }).then(_ => undefined); - } + vscode.debug.startDebugging(session.workspaceFolder, { + type: 'pwa-chrome', + name: 'Chrome Debug', + request: 'launch', + url: uri, + webRoot: args.webRoot || WEB_ROOT + }); break; default: diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index c412586f43c..b2aee5a34ba 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -166,8 +166,8 @@ "view.workbench.scm.missing": "A valid git installation was not detected, more details can be found in the [git output](command:git.showOutput).\nPlease [install git](https://git-scm.com/), or learn more about how to use git and source control in VS Code in [our docs](https://aka.ms/vscode-scm).\nIf you're using a different version control system, you can [search the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22) for additional extensions.", "view.workbench.scm.disabled": "If you would like to use git features, please enable git in your [settings](command:workbench.action.openSettings?%5B%22git.enabled%22%5D).\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.empty": "In order to use git features, you can open a folder containing a git repository or clone from a URL.\n[Open Folder](command:vscode.openFolder)\n[Clone Repository](command:git.clone)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", - "view.workbench.scm.folder": "The folder currently open doesn't have a git repository.\n[Initialize Repository](command:git.init?%5Btrue%5D)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", - "view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Initialize Repository](command:git.init)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", + "view.workbench.scm.folder": "The folder currently open doesn't have a git repository. You can initialize a repository which will enable source control features powered by git.\n[Initialize Repository](command:git.init?%5Btrue%5D)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", + "view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories. You can initialize a repository on a folder which will enable source control features powered by git.\n[Initialize Repository](command:git.init)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.emptyWorkspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Add Folder to Workspace](command:workbench.action.addRootFolder)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.cloneRepository": "You can also clone a repository from a URL. To learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).\n[Clone Repository](command:git.clone)" } diff --git a/extensions/github/package.nls.json b/extensions/github/package.nls.json index 28906a39f0d..34e762b2f9c 100644 --- a/extensions/github/package.nls.json +++ b/extensions/github/package.nls.json @@ -2,6 +2,6 @@ "displayName": "GitHub", "description": "GitHub", "config.gitAuthentication": "Controls whether to enable automatic GitHub authentication for git commands within VS Code.", - "welcome.publishFolder": "You can also directly publish this folder to a GitHub repository.\n[$(github) Publish to GitHub](command:github.publish)", - "welcome.publishWorkspaceFolder": "You can also directly publish a workspace folder to a GitHub repository.\n[$(github) Publish to GitHub](command:github.publish)" + "welcome.publishFolder": "You can also directly publish this folder to a GitHub repository. Once published, you'll have access to source control features powered by git and GitHub.\n[$(github) Publish to GitHub](command:github.publish)", + "welcome.publishWorkspaceFolder": "You can also directly publish a workspace folder to a GitHub repository. Once published, you'll have access to source control features powered by git and GitHub.\n[$(github) Publish to GitHub](command:github.publish)" } diff --git a/extensions/groovy/package.json b/extensions/groovy/package.json index ab2ef0da7ba..f3a0cc17ec8 100644 --- a/extensions/groovy/package.json +++ b/extensions/groovy/package.json @@ -13,7 +13,7 @@ "languages": [{ "id": "groovy", "aliases": ["Groovy", "groovy"], - "extensions": [".groovy", ".gvy", ".gradle", ".jenkinsfile"], + "extensions": [".groovy", ".gvy", ".gradle", ".jenkinsfile", ".nf"], "filenamePatterns": ["Jenkinsfile.*"], "firstLine": "^#!.*\\bgroovy\\b", "configuration": "./language-configuration.json" diff --git a/extensions/java/cgmanifest.json b/extensions/java/cgmanifest.json index 7f0ef8760a8..68e15fac2df 100644 --- a/extensions/java/cgmanifest.json +++ b/extensions/java/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "atom/language-java", "repositoryUrl": "https://github.com/atom/language-java", - "commitHash": "2e179ceac423403eb5bf0eff26884093c3edba6f" + "commitHash": "f631536a4137d65b622f01cebeac72345f9e6f70" } }, "license": "MIT", - "version": "0.31.4" + "version": "0.31.5" } ], "version": 1 diff --git a/extensions/java/syntaxes/java.tmLanguage.json b/extensions/java/syntaxes/java.tmLanguage.json index 3cfa6f56337..11b6cf50a93 100644 --- a/extensions/java/syntaxes/java.tmLanguage.json +++ b/extensions/java/syntaxes/java.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/atom/language-java/commit/2e179ceac423403eb5bf0eff26884093c3edba6f", + "version": "https://github.com/atom/language-java/commit/f631536a4137d65b622f01cebeac72345f9e6f70", "name": "Java", "scopeName": "source.java", "patterns": [ @@ -1785,7 +1785,7 @@ ] }, "variables": { - "begin": "(?x)\n(?=\n (\n \\b(void|boolean|byte|char|short|int|float|long|double)\\b\n |\n (?>(\\w+\\.)*[A-Z_]+\\w*) # e.g. `javax.ws.rs.Response`, or `String`\n )\n \\s*\n (\n <[\\w<>,\\.?\\s\\[\\]]*> # e.g. `HashMap`, or `List`\n )?\n \\s*\n (\n (\\[\\])* # int[][]\n )?\n \\s+\n [A-Za-z_$][\\w$]* # At least one identifier after space\n ([\\w\\[\\],$][\\w\\[\\],\\s]*)? # possibly primitive array or additional identifiers\n \\s*(=|:|;)\n)", + "begin": "(?x)\n(?=\n \\b\n (\n (void|boolean|byte|char|short|int|float|long|double)\n |\n (?>(\\w+\\.)*[A-Z_]+\\w*) # e.g. `javax.ws.rs.Response`, or `String`\n )\n \\b\n \\s*\n (\n <[\\w<>,\\.?\\s\\[\\]]*> # e.g. `HashMap`, or `List`\n )?\n \\s*\n (\n (\\[\\])* # int[][]\n )?\n \\s+\n [A-Za-z_$][\\w$]* # At least one identifier after space\n ([\\w\\[\\],$][\\w\\[\\],\\s]*)? # possibly primitive array or additional identifiers\n \\s*(=|:|;)\n)", "end": "(?=\\=|:|;)", "name": "meta.definition.variable.java", "patterns": [ diff --git a/extensions/php/cgmanifest.json b/extensions/php/cgmanifest.json index ea157efc34e..3c5b9b9ffd3 100644 --- a/extensions/php/cgmanifest.json +++ b/extensions/php/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "language-php", "repositoryUrl": "https://github.com/atom/language-php", - "commitHash": "11cdaf62a9d949d3aca550f1a58c9754de6b5ab0" + "commitHash": "6c0da475f86b45ea990638525f68be7658986546" } }, "license": "MIT", diff --git a/extensions/php/syntaxes/php.tmLanguage.json b/extensions/php/syntaxes/php.tmLanguage.json index a995a1e82ab..54fbd4b7869 100644 --- a/extensions/php/syntaxes/php.tmLanguage.json +++ b/extensions/php/syntaxes/php.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/atom/language-php/commit/11cdaf62a9d949d3aca550f1a58c9754de6b5ab0", + "version": "https://github.com/atom/language-php/commit/6c0da475f86b45ea990638525f68be7658986546", "scopeName": "source.php", "patterns": [ { @@ -1329,6 +1329,17 @@ } ] }, + "named-arguments": { + "match": "(?i)(?<=^|\\(|,)\\s*([a-z_\\x{7f}-\\x{7fffffff}][a-z0-9_\\x{7f}-\\x{7fffffff}]*)\\s*(:)(?!:)", + "captures": { + "1": { + "name": "entity.name.variable.parameter.php" + }, + "2": { + "name": "punctuation.separator.colon.php" + } + } + }, "function-call": { "patterns": [ { @@ -1357,6 +1368,9 @@ }, "name": "meta.function-call.php", "patterns": [ + { + "include": "#named-arguments" + }, { "include": "$self" } @@ -1395,6 +1409,9 @@ }, "name": "meta.function-call.php", "patterns": [ + { + "include": "#named-arguments" + }, { "include": "$self" } @@ -2242,6 +2259,9 @@ }, "name": "meta.method-call.php", "patterns": [ + { + "include": "#named-arguments" + }, { "include": "$self" } @@ -2614,6 +2634,9 @@ }, "name": "meta.method-call.static.php", "patterns": [ + { + "include": "#named-arguments" + }, { "include": "$self" } @@ -3447,6 +3470,18 @@ } }, "patterns": [ + { + "match": "(?i)^\\s*([a-z_\\x{7f}-\\x{7fffffff}][a-z0-9_\\x{7f}-\\x{7fffffff}]*)\\s*(?=:(?!:))", + "captures": { + "1": { + "patterns": [ + { + "include": "$self" + } + ] + } + } + }, { "include": "$self" } diff --git a/extensions/python/package.json b/extensions/python/package.json index e7c75aa4ea1..f39c3d62b23 100644 --- a/extensions/python/package.json +++ b/extensions/python/package.json @@ -15,7 +15,7 @@ "id": "python", "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi", ".pyi", ".ipy"], "aliases": [ "Python", "py" ], - "filenames": [ "Snakefile" ], + "filenames": [ "Snakefile", "SConstruct", "SConscript" ], "firstLine": "^#!\\s*/?.*\\bpython[0-9.-]*\\b", "configuration": "./language-configuration.json" }], diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json index 3ef90a09d14..45e7c7f4e7b 100644 --- a/extensions/sql/cgmanifest.json +++ b/extensions/sql/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Microsoft/vscode-mssql", "repositoryUrl": "https://github.com/Microsoft/vscode-mssql", - "commitHash": "750d30dc48c4c0317b63bb5f1ed3e71487bb84a1" + "commitHash": "61ae0eb21ac53883a23e09913a5ae77a59126ff9" } }, "license": "MIT", diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index bc3b533bcb9..de4dc029f80 100644 --- a/extensions/sql/syntaxes/sql.tmLanguage.json +++ b/extensions/sql/syntaxes/sql.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/vscode-mssql/commit/750d30dc48c4c0317b63bb5f1ed3e71487bb84a1", + "version": "https://github.com/Microsoft/vscode-mssql/commit/61ae0eb21ac53883a23e09913a5ae77a59126ff9", "name": "SQL", "scopeName": "source.sql", "patterns": [ diff --git a/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts b/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts index fdc552a293c..40610b66f11 100644 --- a/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts @@ -306,7 +306,10 @@ class GetErrRequest { public readonly files: ResourceMap, onDone: () => void ) { - const allFiles = coalesce(Array.from(files.entries).map(entry => client.normalizedPath(entry.resource))); + const allFiles = coalesce(Array.from(files.entries) + .filter(entry => client.hasCapabilityForResource(entry.resource, ClientCapability.Semantic)) + .map(entry => client.normalizedPath(entry.resource))); + if (!allFiles.length || !client.capabilities.has(ClientCapability.Semantic)) { this._done = true; setImmediate(onDone); @@ -529,7 +532,7 @@ export default class BufferSyncSupport extends Disposable { this.triggerDiagnostics(); } - public getErr(resources: vscode.Uri[]): any { + public getErr(resources: readonly vscode.Uri[]): any { const handledResources = resources.filter(resource => this.handles(resource)); if (!handledResources.length) { return; diff --git a/extensions/typescript-language-features/src/tsServer/versionManager.ts b/extensions/typescript-language-features/src/tsServer/versionManager.ts index 069d6d683d4..4811fefd3a7 100644 --- a/extensions/typescript-language-features/src/tsServer/versionManager.ts +++ b/extensions/typescript-language-features/src/tsServer/versionManager.ts @@ -5,7 +5,6 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as path from 'path'; import { TypeScriptServiceConfiguration } from '../utils/configuration'; import { Disposable } from '../utils/dispose'; import { ITypeScriptVersionProvider, TypeScriptVersion } from './versionProvider'; @@ -110,7 +109,7 @@ export class TypeScriptVersionManager extends Disposable { run: async () => { await this.workspaceState.update(useWorkspaceTsdkStorageKey, true); const tsConfig = vscode.workspace.getConfiguration('typescript'); - await tsConfig.update('tsdk', path.dirname(version.tsServerPath), false); + await tsConfig.update('tsdk', version.pathLabel, false); this.updateActiveVersion(version); }, }; diff --git a/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts index 15107bc4b5b..760f82d7b09 100644 --- a/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts +++ b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts @@ -104,7 +104,8 @@ export class DiskTypeScriptVersionProvider implements ITypeScriptVersionProvider return [ new TypeScriptVersion(source, serverPath, - DiskTypeScriptVersionProvider.getApiVersion(serverPath)) + DiskTypeScriptVersionProvider.getApiVersion(serverPath), + tsdkPathSetting) ]; } diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 8a59d97105c..58a78ad19b5 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -145,10 +145,15 @@ export interface ITypeScriptServiceClient { /** * Tries to ensure that a vscode document is open on the TS server. * - * Returns the normalized path. + * @return The normalized path or `undefined` if the document is not open on the server. */ toOpenedFilePath(document: vscode.TextDocument): string | undefined; + /** + * Checks if `resource` has a given capability. + */ + hasCapabilityForResource(resource: vscode.Uri, capability: ClientCapability): boolean; + getWorkspaceRootForResource(resource: vscode.Uri): string | undefined; readonly onTsServerStarted: vscode.Event<{ version: TypeScriptVersion, usedApiVersion: API }>; diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index a9e6bf10d7f..c068f94189b 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -673,6 +673,26 @@ export default class TypeScriptServiceClient extends Disposable implements IType return this.toPath(document.uri) || undefined; } + public hasCapabilityForResource(resource: vscode.Uri, capability: ClientCapability): boolean { + switch (capability) { + case ClientCapability.Semantic: + { + switch (resource.scheme) { + case fileSchemes.file: + case fileSchemes.untitled: + return true; + default: + return false; + } + } + case ClientCapability.Syntax: + case ClientCapability.EnhancedSyntax: + { + return true; + } + } + } + public toResource(filepath: string): vscode.Uri { if (filepath.startsWith(this.inMemoryResourcePrefix)) { const resource = vscode.Uri.parse(filepath.slice(1)); diff --git a/extensions/typescript-language-features/src/utils/fileSchemes.ts b/extensions/typescript-language-features/src/utils/fileSchemes.ts index 46b8467879d..d465a60326e 100644 --- a/extensions/typescript-language-features/src/utils/fileSchemes.ts +++ b/extensions/typescript-language-features/src/utils/fileSchemes.ts @@ -6,8 +6,6 @@ export const file = 'file'; export const untitled = 'untitled'; export const git = 'git'; -export const pr = 'pr'; -export const privateScheme = 'private'; /** Live share scheme */ export const vsls = 'vsls'; export const walkThroughSnippet = 'walkThroughSnippet'; @@ -22,7 +20,5 @@ export const semanticSupportedSchemes = [ */ export const disabledSchemes = new Set([ git, - vsls, - privateScheme, - pr, + vsls ]); diff --git a/package.json b/package.json index 5e05dbde40c..1d04db1ccf5 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "native-is-elevated": "0.4.1", "native-keymap": "2.2.0", "native-watchdog": "1.3.0", - "node-pty": "0.10.0-beta8", + "node-pty": "0.10.0-beta17", "semver-umd": "^5.5.7", "spdlog": "^0.11.1", "sudo-prompt": "9.1.1", @@ -68,13 +68,13 @@ "vscode-nsfw": "1.2.8", "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", - "vscode-ripgrep": "^1.8.0", + "vscode-ripgrep": "^1.9.0", "vscode-sqlite3": "4.0.10", "vscode-textmate": "5.2.0", - "xterm": "4.9.0-beta.25", + "xterm": "4.9.0-beta.32", "xterm-addon-search": "0.7.0", "xterm-addon-unicode11": "0.2.0", - "xterm-addon-webgl": "0.9.0-beta.1", + "xterm-addon-webgl": "0.9.0-beta.4", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, @@ -110,7 +110,7 @@ "css-loader": "^3.2.0", "debounce": "^1.0.0", "deemon": "^1.4.0", - "electron": "9.2.1", + "electron": "9.3.0", "eslint": "6.8.0", "eslint-plugin-jsdoc": "^19.1.0", "event-stream": "3.3.4", diff --git a/product.json b/product.json index 30a7f0708ea..7651fc50bb5 100644 --- a/product.json +++ b/product.json @@ -61,7 +61,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.62", + "version": "0.0.63", "repo": "https://github.com/Microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", diff --git a/remote/package.json b/remote/package.json index 187c951af64..0a7b6f7e0a1 100644 --- a/remote/package.json +++ b/remote/package.json @@ -12,18 +12,18 @@ "jschardet": "2.2.1", "minimist": "^1.2.5", "native-watchdog": "1.3.0", - "node-pty": "0.10.0-beta8", + "node-pty": "0.10.0-beta17", "semver-umd": "^5.5.7", "spdlog": "^0.11.1", "vscode-nsfw": "1.2.8", "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", - "vscode-ripgrep": "^1.8.0", + "vscode-ripgrep": "^1.9.0", "vscode-textmate": "5.2.0", - "xterm": "4.9.0-beta.25", + "xterm": "4.9.0-beta.32", "xterm-addon-search": "0.7.0", "xterm-addon-unicode11": "0.2.0", - "xterm-addon-webgl": "0.9.0-beta.1", + "xterm-addon-webgl": "0.9.0-beta.4", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index fc4855b4ab7..9db946c5bbb 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -7,9 +7,9 @@ "semver-umd": "^5.5.7", "vscode-oniguruma": "1.3.1", "vscode-textmate": "5.2.0", - "xterm": "4.9.0-beta.25", + "xterm": "4.9.0-beta.32", "xterm-addon-search": "0.7.0", "xterm-addon-unicode11": "0.2.0", - "xterm-addon-webgl": "0.9.0-beta.1" + "xterm-addon-webgl": "0.9.0-beta.4" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index a5498289c43..400d4d629c4 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -37,12 +37,12 @@ xterm-addon-unicode11@0.2.0: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0.tgz#9ed0c482b353908bba27778893ca80823382737c" integrity sha512-rjFDItPc/IDoSiEnoDFwKroNwLD/7t9vYKENjrcKVZg5tgJuuUj8D4rZtP6iVCjSB1LTLYmUs4L/EmCqIyLR/Q== -xterm-addon-webgl@0.9.0-beta.1: - version "0.9.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.9.0-beta.1.tgz#e0a4e9b71db7ef8182064236844caf32833214c2" - integrity sha512-yyIYfehzaqaOTOTjSapA3sEcaGDIPn0xAiWyRIWyW5zwTZIo7FA+O4IzhgAT9b7ioJWLcPoHPUA0OFNHAx20vQ== +xterm-addon-webgl@0.9.0-beta.4: + version "0.9.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.9.0-beta.4.tgz#5f5fde50db5c06b116471bcf56ad9930884b36fa" + integrity sha512-GuCvF7Eg1nKLX6zUbJLkt5cqeeccUjf/G6fugCfrkR0EWWC6Ik5mEsEOs5UWm9vvY2kX9t16BdCrgqp8KJegEg== -xterm@4.9.0-beta.25: - version "4.9.0-beta.25" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.25.tgz#c229a144184ab7b3c3afdaff76f9d81bac58933b" - integrity sha512-NJHwuS7Qsoq/Yuobl+Au4rgol/oBNj4pGoR2R6IRnXODS5RDE4spHDofWKiUkDMlNZtPPku1W7k1kFnonDIhhg== +xterm@4.9.0-beta.32: + version "4.9.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.32.tgz#d1243d3be211cc06aad3418e696e4eced995c20c" + integrity sha512-jloHNBnj6XRJt+oPkapvrXJZVsYq6se/PEgzErl0iZn9qzSB3jmWE4byumoEjXJR6EgU5ZOmNljeeEDA9jO/jA== diff --git a/remote/yarn.lock b/remote/yarn.lock index 748e983bd3c..c5f19e6bbc9 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -294,10 +294,10 @@ node-addon-api@1.6.2: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.2.tgz#d8aad9781a5cfc4132cc2fecdbdd982534265217" integrity sha512-479Bjw9nTE5DdBSZZWprFryHGjUaQC31y1wHo19We/k0BZlrmhqQitWoUL0cD8+scljCbIUL+E58oRDEakdGGA== -node-pty@0.10.0-beta8: - version "0.10.0-beta8" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta8.tgz#f4aa56c71a794f4580373a3030dfebd25240c6db" - integrity sha512-Ul/hLsadC0SvvShxpne+kq2ebSMcitewlNhrwoXXBvFdCqxJt7Ai1AgMhH7AKBUp06uBeYXThJ2ihTszrkdnYw== +node-pty@0.10.0-beta17: + version "0.10.0-beta17" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta17.tgz#962d4a3f4dc6772385e0cad529c209cef3bc79e6" + integrity sha512-tn7EANQacnAvnOQCImvgag1DL0tVmUoY/1yIZbh3u/BBpvCcGHLZJNn7TXheodRLr6hmGSUS2VbfcUr9p0gOug== dependencies: nan "^2.14.0" @@ -410,10 +410,10 @@ vscode-proxy-agent@^0.5.2: https-proxy-agent "^2.2.3" socks-proxy-agent "^4.0.1" -vscode-ripgrep@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.8.0.tgz#dfe7c2ae2a2032df8a8108765c2feef73474888a" - integrity sha512-/Q5XtePkTLLi8yplr5ai24pVEymRF62xH9xXrtj35GTaDCJg3zq1s1/L1UqhVbfNDv4OcMBYjyIAt/quEi3d5w== +vscode-ripgrep@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.9.0.tgz#d6cdea4d290f3c2919472cdcfe2440d5fb1f99db" + integrity sha512-7jyAC/NNfvMPZgCVkyqIn0STYJ7wIk3PF2qA2cX1sEutx1g/e2VtgKAodXnfpreJq4993JT/BSIigOv/0lBSzg== dependencies: https-proxy-agent "^4.0.0" proxy-from-env "^1.1.0" @@ -445,15 +445,15 @@ xterm-addon-unicode11@0.2.0: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0.tgz#9ed0c482b353908bba27778893ca80823382737c" integrity sha512-rjFDItPc/IDoSiEnoDFwKroNwLD/7t9vYKENjrcKVZg5tgJuuUj8D4rZtP6iVCjSB1LTLYmUs4L/EmCqIyLR/Q== -xterm-addon-webgl@0.9.0-beta.1: - version "0.9.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.9.0-beta.1.tgz#e0a4e9b71db7ef8182064236844caf32833214c2" - integrity sha512-yyIYfehzaqaOTOTjSapA3sEcaGDIPn0xAiWyRIWyW5zwTZIo7FA+O4IzhgAT9b7ioJWLcPoHPUA0OFNHAx20vQ== +xterm-addon-webgl@0.9.0-beta.4: + version "0.9.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.9.0-beta.4.tgz#5f5fde50db5c06b116471bcf56ad9930884b36fa" + integrity sha512-GuCvF7Eg1nKLX6zUbJLkt5cqeeccUjf/G6fugCfrkR0EWWC6Ik5mEsEOs5UWm9vvY2kX9t16BdCrgqp8KJegEg== -xterm@4.9.0-beta.25: - version "4.9.0-beta.25" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.25.tgz#c229a144184ab7b3c3afdaff76f9d81bac58933b" - integrity sha512-NJHwuS7Qsoq/Yuobl+Au4rgol/oBNj4pGoR2R6IRnXODS5RDE4spHDofWKiUkDMlNZtPPku1W7k1kFnonDIhhg== +xterm@4.9.0-beta.32: + version "4.9.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.32.tgz#d1243d3be211cc06aad3418e696e4eced995c20c" + integrity sha512-jloHNBnj6XRJt+oPkapvrXJZVsYq6se/PEgzErl0iZn9qzSB3jmWE4byumoEjXJR6EgU5ZOmNljeeEDA9jO/jA== yauzl@^2.9.2: version "2.10.0" diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 9349efc2792..fe50083857a 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -12,7 +12,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; -import { coalesce } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; import { Schemas, RemoteAuthorities } from 'vs/base/common/network'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; @@ -1012,20 +1011,23 @@ export function prepend(parent: HTMLElement, child: T): T { return child; } -const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((\.([\w\-]+))*)/; -export function reset(parent: HTMLElement, ...children: Array) { +/** + * Removes all children from `parent` and appends `children` + */ +export function reset(parent: HTMLElement, ...children: Array) { parent.innerText = ''; - coalesce(children) - .forEach(child => { - if (child instanceof Node) { - parent.appendChild(child); - } else { - parent.appendChild(document.createTextNode(child as string)); - } - }); + for (const child of children) { + if (child instanceof Node) { + parent.appendChild(child); + } else if (typeof child === 'string') { + parent.appendChild(document.createTextNode(child)); + } + } } +const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((\.([\w\-]+))*)/; + export enum Namespace { HTML = 'http://www.w3.org/1999/xhtml', SVG = 'http://www.w3.org/2000/svg' @@ -1075,14 +1077,13 @@ function _$(namespace: Namespace, description: string, attrs? } }); - coalesce(children) - .forEach(child => { - if (child instanceof Node) { - result.appendChild(child); - } else { - result.appendChild(document.createTextNode(child as string)); - } - }); + for (const child of children) { + if (child instanceof Node) { + result.appendChild(child); + } else if (typeof child === 'string') { + result.appendChild(document.createTextNode(child)); + } + } return result as T; } diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 59225493378..fd91a3cb625 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -118,7 +118,7 @@ export class BreadcrumbsWidget { dispose(): void { this._disposables.dispose(); - dispose(this._pendingLayout); + this._pendingLayout?.dispose(); this._onDidSelectItem.dispose(); this._onDidFocusItem.dispose(); this._onDidChangeFocus.dispose(); @@ -131,9 +131,7 @@ export class BreadcrumbsWidget { if (dim && dom.Dimension.equals(dim, this._dimension)) { return; } - if (this._pendingLayout) { - this._pendingLayout.dispose(); - } + this._pendingLayout?.dispose(); if (dim) { // only measure this._pendingLayout = this._updateDimensions(dim); diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index e408f21afd2..f5599033976 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as objects from 'vs/base/common/objects'; -import { renderCodicons } from 'vs/base/common/codicons'; -import { escape } from 'vs/base/common/strings'; +import * as dom from 'vs/base/browser/dom'; +import { renderCodiconsAsElement } from 'vs/base/browser/codicons'; export interface IHighlight { start: number; @@ -15,7 +15,7 @@ export interface IHighlight { export class HighlightedLabel { - private domNode: HTMLElement; + private readonly domNode: HTMLElement; private text: string = ''; private title: string = ''; private highlights: IHighlight[] = []; @@ -44,10 +44,6 @@ export class HighlightedLabel { return; } - if (!Array.isArray(highlights)) { - highlights = []; - } - this.text = text; this.title = title; this.highlights = highlights; @@ -56,7 +52,7 @@ export class HighlightedLabel { private render(): void { - let htmlContent = ''; + const children: HTMLSpanElement[] = []; let pos = 0; for (const highlight of this.highlights) { @@ -64,31 +60,26 @@ export class HighlightedLabel { continue; } if (pos < highlight.start) { - htmlContent += ''; const substring = this.text.substring(pos, highlight.start); - htmlContent += this.supportCodicons ? renderCodicons(escape(substring)) : escape(substring); - htmlContent += ''; + children.push(dom.$('span', undefined, ...this.supportCodicons ? renderCodiconsAsElement(substring) : [substring])); pos = highlight.end; } - if (highlight.extraClasses) { - htmlContent += ``; - } else { - htmlContent += ``; - } + const substring = this.text.substring(highlight.start, highlight.end); - htmlContent += this.supportCodicons ? renderCodicons(escape(substring)) : escape(substring); - htmlContent += ''; + const element = dom.$('span.highlight', undefined, ...this.supportCodicons ? renderCodiconsAsElement(substring) : [substring]); + if (highlight.extraClasses) { + element.classList.add(highlight.extraClasses); + } + children.push(element); pos = highlight.end; } if (pos < this.text.length) { - htmlContent += ''; - const substring = this.text.substring(pos); - htmlContent += this.supportCodicons ? renderCodicons(escape(substring)) : escape(substring); - htmlContent += ''; + const substring = this.text.substring(pos,); + children.push(dom.$('span', undefined, ...this.supportCodicons ? renderCodiconsAsElement(substring) : [substring])); } - this.domNode.innerHTML = htmlContent; + dom.reset(this.domNode, ...children); if (this.title) { this.domNode.title = this.title; } else { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 9821d6bcc6f..8981236c613 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -896,7 +896,9 @@ export class ListView implements ISpliceable, IDisposable { this.render(previousRenderRange, e.scrollTop, e.height, e.scrollLeft, e.scrollWidth); if (this.supportDynamicHeights) { - this._rerender(e.scrollTop, e.height); + // Don't update scrollTop from within an scroll event + // so we don't break smooth scrolling. #104144 + this._rerender(e.scrollTop, e.height, false); } } catch (err) { console.error('Got bad scroll event:', e); @@ -1166,7 +1168,7 @@ export class ListView implements ISpliceable, IDisposable { * Given a stable rendered state, checks every rendered element whether it needs * to be probed for dynamic height. Adjusts scroll height and top if necessary. */ - private _rerender(renderTop: number, renderHeight: number): void { + private _rerender(renderTop: number, renderHeight: number, updateScrollTop: boolean = true): void { const previousRenderRange = this.getRenderRange(renderTop, renderHeight); // Let's remember the second element's position, this helps in scrolling up @@ -1232,7 +1234,7 @@ export class ListView implements ISpliceable, IDisposable { } } - if (typeof anchorElementIndex === 'number') { + if (updateScrollTop && typeof anchorElementIndex === 'number') { this.scrollTop = this.elementTop(anchorElementIndex) - anchorElementTopDelta!; } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index a8f9bb24c5a..1d29e9a3c00 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -503,42 +503,15 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi // Iterate over detailed descriptions, find max height private measureMaxDetailsHeight(): number { - let maxDetailsPaneHeight = 0; - this.options.forEach((option, index) => { - - this.selectionDetailsPane.innerText = ''; - - if (option.description) { - if (option.descriptionIsMarkdown) { - this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(option.description)); - } else { - this.selectionDetailsPane.innerText = option.description; - } - this.selectionDetailsPane.style.display = 'block'; - } else { - this.selectionDetailsPane.style.display = 'none'; - } + this.options.forEach((_option, index) => { + this.updateDetail(index); if (this.selectionDetailsPane.offsetHeight > maxDetailsPaneHeight) { maxDetailsPaneHeight = this.selectionDetailsPane.offsetHeight; } }); - // Reset description to selected - - this.selectionDetailsPane.innerText = ''; - const description = this.options[this.selected].description || null; - const descriptionIsMarkdown = this.options[this.selected].descriptionIsMarkdown || null; - - if (description) { - if (descriptionIsMarkdown) { - this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(description)); - } else { - this.selectionDetailsPane.innerText = description; - } - this.selectionDetailsPane.style.display = 'block'; - } return maxDetailsPaneHeight; } @@ -676,6 +649,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.selectDropDownContainer.style.height = (listHeight + verticalPadding) + 'px'; } + this.updateDetail(this.selected); + this.selectDropDownContainer.style.width = selectOptimalWidth; // Maintain focus outline on parent select as well as list container - tabindex for focus @@ -870,8 +845,11 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi return; } + this.updateDetail(e.indexes[0]); + } + + private updateDetail(selectedIndex: number): void { this.selectionDetailsPane.innerText = ''; - const selectedIndex = e.indexes[0]; const description = this.options[selectedIndex].description; const descriptionIsMarkdown = this.options[selectedIndex].descriptionIsMarkdown; diff --git a/src/vs/base/common/skipList.ts b/src/vs/base/common/skipList.ts index 1243ebdddf7..6b3bd79ef4c 100644 --- a/src/vs/base/common/skipList.ts +++ b/src/vs/base/common/skipList.ts @@ -22,7 +22,7 @@ export class SkipList implements Map { readonly [Symbol.toStringTag] = 'SkipList'; private _maxLevel: number; - private _level: number = 1; + private _level: number = 0; private _header: Node; private _size: number = 0; @@ -122,7 +122,7 @@ export class SkipList implements Map { private static _search(list: SkipList, searchKey: K, comparator: Comparator) { let x = list._header; - for (let i = list._level; i >= 0; i--) { + for (let i = list._level - 1; i >= 0; i--) { while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) { x = x.forward[i]; } @@ -137,7 +137,7 @@ export class SkipList implements Map { private static _insert(list: SkipList, searchKey: K, value: V, comparator: Comparator) { let update: Node[] = []; let x = list._header; - for (let i = list._level; i >= 0; i--) { + for (let i = list._level - 1; i >= 0; i--) { while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) { x = x.forward[i]; } @@ -152,13 +152,13 @@ export class SkipList implements Map { // insert let lvl = SkipList._randomLevel(list); if (lvl > list._level) { - for (let i = list._level + 1; i <= lvl; i++) { + for (let i = list._level; i < lvl; i++) { update[i] = list._header; } list._level = lvl; } x = new Node(lvl, searchKey, value); - for (let i = 0; i <= lvl; i++) { + for (let i = 0; i < lvl; i++) { x.forward[i] = update[i].forward[i]; update[i].forward[i] = x; } @@ -177,7 +177,7 @@ export class SkipList implements Map { private static _delete(list: SkipList, searchKey: K, comparator: Comparator) { let update: Node[] = []; let x = list._header; - for (let i = list._level; i >= 0; i--) { + for (let i = list._level - 1; i >= 0; i--) { while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) { x = x.forward[i]; } @@ -194,7 +194,7 @@ export class SkipList implements Map { } update[i].forward[i] = x.forward[i]; } - while (list._level >= 1 && list._header.forward[list._level] === NIL) { + while (list._level > 0 && list._header.forward[list._level - 1] === NIL) { list._level -= 1; } return true; diff --git a/src/vs/base/parts/request/browser/request.ts b/src/vs/base/parts/request/browser/request.ts index 5dd4fdd449c..1c2499ba404 100644 --- a/src/vs/base/parts/request/browser/request.ts +++ b/src/vs/base/parts/request/browser/request.ts @@ -5,13 +5,15 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; -import { assign } from 'vs/base/common/objects'; import { VSBuffer, bufferToStream } from 'vs/base/common/buffer'; import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; export function request(options: IRequestOptions, token: CancellationToken): Promise { if (options.proxyAuthorization) { - options.headers = assign(options.headers || {}, { 'Proxy-Authorization': options.proxyAuthorization }); + options.headers = { + ...(options.headers || {}), + 'Proxy-Authorization': options.proxyAuthorization + }; } const xhr = new XMLHttpRequest(); diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 631ddb651e6..f44b74ffc7d 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -76,7 +76,7 @@ bootstrapWindow.load([ /** * @param {{ * partsSplashPath?: string, - * colorScheme: (1 | 2 | 3), + * colorScheme: ('light' | 'dark' | 'hc'), * autoDetectHighContrast?: boolean, * extensionDevelopmentPath?: string[], * folderUri?: object, @@ -96,7 +96,7 @@ function showPartsSplash(configuration) { } // high contrast mode has been turned on from the outside, e.g. OS -> ignore stored colors and layouts - const isHighContrast = configuration.colorScheme === 3 /* ColorScheme.HIGH_CONTRAST */ && configuration.autoDetectHighContrast; + const isHighContrast = configuration.colorScheme === 'hc' /* ColorScheme.HIGH_CONTRAST */ && configuration.autoDetectHighContrast; if (data && isHighContrast && data.baseTheme !== 'hc-black') { data = undefined; } diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts index c1e2a84946a..ca071cf2836 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -55,7 +55,7 @@ export class ProxyAuthHandler extends Disposable { skipTaskbar: true, resizable: false, width: 450, - height: 220, + height: 225, show: true, title: 'VS Code', webPreferences: { diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 66f1cd08c3e..d0ae1450567 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -184,7 +184,7 @@ class CodeMain { environmentService.logsPath, environmentService.globalStorageHome.fsPath, environmentService.workspaceStorageHome.fsPath, - environmentService.backupHome.fsPath + environmentService.backupHome ].map((path): undefined | Promise => path ? mkdirp(path) : undefined)); // Configuration service diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 0df1b6af683..40cbd5b9a43 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -15,7 +15,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import product from 'vs/platform/product/common/product'; -import { IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, zoomLevelToZoomFactor, INativeWindowConfiguration, ColorScheme } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, zoomLevelToZoomFactor, INativeWindowConfiguration } from 'vs/platform/windows/common/windows'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { ICodeWindow, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; @@ -34,6 +34,7 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; import { IFileService } from 'vs/platform/files/common/files'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; export interface IWindowCreationOptions { state: IWindowState; @@ -784,7 +785,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { windowConfiguration.fullscreen = this.isFullScreen; // Set Accessibility Config - windowConfiguration.colorScheme = (nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors) ? ColorScheme.HIGH_CONTRAST : nativeTheme.shouldUseDarkColors ? ColorScheme.DARK : ColorScheme.DEFAULT; + windowConfiguration.colorScheme = (nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors) ? ColorScheme.HIGH_CONTRAST : nativeTheme.shouldUseDarkColors ? ColorScheme.DARK : ColorScheme.LIGHT; windowConfiguration.autoDetectHighContrast = windowConfig?.autoDetectHighContrast ?? true; windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled; diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 65798b74acc..d75928f24ae 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -15,7 +15,7 @@ import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; import { CharacterMapping, ForeignElementType, RenderLineInput, renderViewLine, LineRange } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; -import { HIGH_CONTRAST, ThemeType } from 'vs/platform/theme/common/themeService'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; import { EditorOption, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; const canUseFastRenderedViewLine = (function () { @@ -71,7 +71,7 @@ export class DomReadingContext { } export class ViewLineOptions { - public readonly themeType: ThemeType; + public readonly themeType: ColorScheme; public readonly renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all'; public readonly renderControlCharacters: boolean; public readonly spaceWidth: number; @@ -83,7 +83,7 @@ export class ViewLineOptions { public readonly stopRenderingLineAfter: number; public readonly fontLigatures: string; - constructor(config: IConfiguration, themeType: ThemeType) { + constructor(config: IConfiguration, themeType: ColorScheme) { this.themeType = themeType; const options = config.options; const fontInfo = options.get(EditorOption.fontInfo); @@ -163,7 +163,7 @@ export class ViewLine implements IVisibleLine { this._options = newOptions; } public onSelectionChanged(): boolean { - if (alwaysRenderInlineSelection || this._options.themeType === HIGH_CONTRAST || this._options.renderWhitespace === 'selection') { + if (alwaysRenderInlineSelection || this._options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace === 'selection') { this._isMaybeInvalid = true; return true; } @@ -184,7 +184,7 @@ export class ViewLine implements IVisibleLine { // Only send selection information when needed for rendering whitespace let selectionsOnLine: LineRange[] | null = null; - if (alwaysRenderInlineSelection || options.themeType === HIGH_CONTRAST || this._options.renderWhitespace === 'selection') { + if (alwaysRenderInlineSelection || options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace === 'selection') { const selections = viewportData.selections; for (const selection of selections) { @@ -197,7 +197,7 @@ export class ViewLine implements IVisibleLine { const endColumn = (selection.endLineNumber === lineNumber ? selection.endColumn : lineData.maxColumn); if (startColumn < endColumn) { - if (options.themeType === HIGH_CONTRAST || this._options.renderWhitespace !== 'selection') { + if (options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace !== 'selection') { actualInlineDecorations.push(new LineDecoration(startColumn, endColumn, 'inline-selected-text', InlineDecorationType.Regular)); } else { if (!selectionsOnLine) { diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index 4278ec10e3a..3a19ca1acf8 100644 --- a/src/vs/editor/common/modes/languageFeatureRegistry.ts +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -4,7 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; +import { hash } from 'vs/base/common/hash'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { LRUCache } from 'vs/base/common/map'; +import { MovingAverage } from 'vs/base/common/numbers'; import { ITextModel } from 'vs/editor/common/model'; import { LanguageSelector, score } from 'vs/editor/common/modes/languageSelector'; import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; @@ -174,3 +177,46 @@ export class LanguageFeatureRegistry { } } } + + +/** + * Keeps moving average per model and set of providers so that requests + * can be debounce according to the provider performance + */ +export class LanguageFeatureRequestDelays { + + private readonly _cache = new LRUCache(50, 0.7); + + constructor( + private readonly _registry: LanguageFeatureRegistry, + readonly min: number, + readonly max: number = Number.MAX_SAFE_INTEGER, + ) { } + + private _key(model: ITextModel): string { + return model.id + hash(this._registry.all(model)); + } + + private _clamp(value: number | undefined): number { + if (value === undefined) { + return this.min; + } else { + return Math.min(this.max, Math.max(this.min, Math.floor(value * 1.3))); + } + } + + get(model: ITextModel): number { + const key = this._key(model); + return this._clamp(this._cache.get(key)?.value); + } + + update(model: ITextModel, value: number) { + const key = this._key(model); + let avg = this._cache.get(key); + if (!avg) { + avg = new MovingAverage(); + this._cache.set(key, avg); + } + avg.update(value); + } +} diff --git a/src/vs/editor/common/modes/supports/tokenization.ts b/src/vs/editor/common/modes/supports/tokenization.ts index e701b60f0a9..12566c3b00e 100644 --- a/src/vs/editor/common/modes/supports/tokenization.ts +++ b/src/vs/editor/common/modes/supports/tokenization.ts @@ -399,10 +399,10 @@ export function generateTokensCSSForColorMap(colorMap: Color[]): string { let rules: string[] = []; for (let i = 1, len = colorMap.length; i < len; i++) { let color = colorMap[i]; - rules[i] = `.monaco-editor .mtk${i} { color: ${color}; }`; + rules[i] = `.mtk${i} { color: ${color}; }`; } - rules.push('.monaco-editor .mtki { font-style: italic; }'); - rules.push('.monaco-editor .mtkb { font-weight: bold; }'); - rules.push('.monaco-editor .mtku { text-decoration: underline; text-underline-position: under; }'); + rules.push('.mtki { font-style: italic; }'); + rules.push('.mtkb { font-weight: bold; }'); + rules.push('.mtku { text-decoration: underline; text-underline-position: under; }'); return rules.join('\n'); } diff --git a/src/vs/editor/common/view/viewContext.ts b/src/vs/editor/common/view/viewContext.ts index 74628ebb857..b888fbec6ad 100644 --- a/src/vs/editor/common/view/viewContext.ts +++ b/src/vs/editor/common/view/viewContext.ts @@ -6,15 +6,16 @@ import { IConfiguration } from 'vs/editor/common/editorCommon'; import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; import { IViewLayout, IViewModel } from 'vs/editor/common/viewModel/viewModel'; -import { IColorTheme, ThemeType } from 'vs/platform/theme/common/themeService'; +import { IColorTheme } from 'vs/platform/theme/common/themeService'; import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; import { Color } from 'vs/base/common/color'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; export class EditorTheme { private _theme: IColorTheme; - public get type(): ThemeType { + public get type(): ColorScheme { return this._theme.type; } diff --git a/src/vs/editor/contrib/codelens/codelens.ts b/src/vs/editor/contrib/codelens/codelens.ts index 76c9cd84fd7..fbcfb19913b 100644 --- a/src/vs/editor/contrib/codelens/codelens.ts +++ b/src/vs/editor/contrib/codelens/codelens.ts @@ -36,44 +36,47 @@ export class CodeLensModel { } } -export function getCodeLensData(model: ITextModel, token: CancellationToken): Promise { +export async function getCodeLensModel(model: ITextModel, token: CancellationToken): Promise { const provider = CodeLensProviderRegistry.ordered(model); const providerRanks = new Map(); const result = new CodeLensModel(); - const promises = provider.map((provider, i) => { + const promises = provider.map(async (provider, i) => { providerRanks.set(provider, i); - return Promise.resolve(provider.provideCodeLenses(model, token)) - .then(list => list && result.add(list, provider)) - .catch(onUnexpectedExternalError); - }); - - return Promise.all(promises).then(() => { - - result.lenses = mergeSort(result.lenses, (a, b) => { - // sort by lineNumber, provider-rank, and column - if (a.symbol.range.startLineNumber < b.symbol.range.startLineNumber) { - return -1; - } else if (a.symbol.range.startLineNumber > b.symbol.range.startLineNumber) { - return 1; - } else if (providerRanks.get(a.provider)! < providerRanks.get(b.provider)!) { - return -1; - } else if (providerRanks.get(a.provider)! > providerRanks.get(b.provider)!) { - return 1; - } else if (a.symbol.range.startColumn < b.symbol.range.startColumn) { - return -1; - } else if (a.symbol.range.startColumn > b.symbol.range.startColumn) { - return 1; - } else { - return 0; + try { + const list = await Promise.resolve(provider.provideCodeLenses(model, token)); + if (list) { + result.add(list, provider); } - }); - - return result; + } catch (err) { + onUnexpectedExternalError(err); + } }); + + await Promise.all(promises); + + result.lenses = mergeSort(result.lenses, (a, b) => { + // sort by lineNumber, provider-rank, and column + if (a.symbol.range.startLineNumber < b.symbol.range.startLineNumber) { + return -1; + } else if (a.symbol.range.startLineNumber > b.symbol.range.startLineNumber) { + return 1; + } else if ((providerRanks.get(a.provider)!) < (providerRanks.get(b.provider)!)) { + return -1; + } else if ((providerRanks.get(a.provider)!) > (providerRanks.get(b.provider)!)) { + return 1; + } else if (a.symbol.range.startColumn < b.symbol.range.startColumn) { + return -1; + } else if (a.symbol.range.startColumn > b.symbol.range.startColumn) { + return 1; + } else { + return 0; + } + }); + return result; } registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { @@ -90,7 +93,7 @@ registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { const result: CodeLens[] = []; const disposables = new DisposableStore(); - return getCodeLensData(model, CancellationToken.None).then(value => { + return getCodeLensModel(model, CancellationToken.None).then(value => { disposables.add(value); let resolve: Promise[] = []; diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 14e21796a7c..e61869e3c80 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -5,14 +5,14 @@ import { CancelablePromise, RunOnceScheduler, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; -import { toDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; +import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { StableEditorScrollState } from 'vs/editor/browser/core/editorState'; import { ICodeEditor, MouseTargetType, IViewZoneChangeAccessor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution, ServicesAccessor, registerEditorAction, EditorAction } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; import { CodeLensProviderRegistry, CodeLens, Command } from 'vs/editor/common/modes'; -import { CodeLensModel, getCodeLensData, CodeLensItem } from 'vs/editor/contrib/codelens/codelens'; +import { CodeLensModel, getCodeLensModel, CodeLensItem } from 'vs/editor/contrib/codelens/codelens'; import { CodeLensWidget, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -23,24 +23,25 @@ import { hash } from 'vs/base/common/hash'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { localize } from 'vs/nls'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; export class CodeLensContribution implements IEditorContribution { - public static readonly ID: string = 'css.editor.codeLens'; + static readonly ID: string = 'css.editor.codeLens'; - private _isEnabled: boolean; - - private readonly _globalToDispose = new DisposableStore(); + private readonly _disposables = new DisposableStore(); private readonly _localToDispose = new DisposableStore(); private readonly _styleElement: HTMLStyleElement; private readonly _styleClassName: string; - private _lenses: CodeLensWidget[] = []; - private _currentFindCodeLensSymbolsPromise: CancelablePromise | undefined; + private readonly _lenses: CodeLensWidget[] = []; + + private readonly _getCodeLensModelDelays = new LanguageFeatureRequestDelays(CodeLensProviderRegistry, 250, 2500); + private _getCodeLensModelPromise: CancelablePromise | undefined; private _oldCodeLensModels = new DisposableStore(); private _currentCodeLensModel: CodeLensModel | undefined; - private _modelChangeCounter: number = 0; - private _currentResolveCodeLensSymbolsPromise: CancelablePromise | undefined; - private _detectVisibleLenses: RunOnceScheduler | undefined; + private readonly _resolveCodeLensesDelays = new LanguageFeatureRequestDelays(CodeLensProviderRegistry, 250, 2500); + private readonly _resolveCodeLensesScheduler = new RunOnceScheduler(() => this._resolveCodeLensesInViewport(), this._resolveCodeLensesDelays.min); + private _resolveCodeLensesPromise: CancelablePromise | undefined; constructor( private readonly _editor: ICodeEditor, @@ -48,23 +49,18 @@ export class CodeLensContribution implements IEditorContribution { @INotificationService private readonly _notificationService: INotificationService, @ICodeLensCache private readonly _codeLensCache: ICodeLensCache ) { - this._isEnabled = this._editor.getOption(EditorOption.codeLens); - this._globalToDispose.add(this._editor.onDidChangeModel(() => this._onModelChange())); - this._globalToDispose.add(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); - this._globalToDispose.add(this._editor.onDidChangeConfiguration(() => { - const prevIsEnabled = this._isEnabled; - this._isEnabled = this._editor.getOption(EditorOption.codeLens); - if (prevIsEnabled !== this._isEnabled) { - this._onModelChange(); - } - })); - this._globalToDispose.add(CodeLensProviderRegistry.onDidChange(this._onModelChange, this)); - this._globalToDispose.add(this._editor.onDidChangeConfiguration(e => { + this._disposables.add(this._editor.onDidChangeModel(() => this._onModelChange())); + this._disposables.add(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); + this._disposables.add(this._editor.onDidChangeConfiguration((e) => { if (e.hasChanged(EditorOption.fontInfo)) { this._updateLensStyle(); } + if (e.hasChanged(EditorOption.codeLens)) { + this._onModelChange(); + } })); + this._disposables.add(CodeLensProviderRegistry.onDidChange(this._onModelChange, this)); this._onModelChange(); this._styleClassName = '_' + hash(this._editor.getId()).toString(16); @@ -78,9 +74,9 @@ export class CodeLensContribution implements IEditorContribution { dispose(): void { this._localDispose(); - this._globalToDispose.dispose(); + this._disposables.dispose(); this._oldCodeLensModels.dispose(); - dispose(this._currentCodeLensModel); + this._currentCodeLensModel?.dispose(); } private _updateLensStyle(): void { @@ -99,18 +95,13 @@ export class CodeLensContribution implements IEditorContribution { } private _localDispose(): void { - if (this._currentFindCodeLensSymbolsPromise) { - this._currentFindCodeLensSymbolsPromise.cancel(); - this._currentFindCodeLensSymbolsPromise = undefined; - this._modelChangeCounter++; - } - if (this._currentResolveCodeLensSymbolsPromise) { - this._currentResolveCodeLensSymbolsPromise.cancel(); - this._currentResolveCodeLensSymbolsPromise = undefined; - } + this._getCodeLensModelPromise?.cancel(); + this._getCodeLensModelPromise = undefined; + this._resolveCodeLensesPromise?.cancel(); + this._resolveCodeLensesPromise = undefined; this._localToDispose.clear(); this._oldCodeLensModels.clear(); - dispose(this._currentCodeLensModel); + this._currentCodeLensModel?.dispose(); } private _onModelChange(): void { @@ -122,7 +113,7 @@ export class CodeLensContribution implements IEditorContribution { return; } - if (!this._isEnabled) { + if (!this._editor.getOption(EditorOption.codeLens)) { return; } @@ -153,34 +144,33 @@ export class CodeLensContribution implements IEditorContribution { } } - const detectVisibleLenses = this._detectVisibleLenses = new RunOnceScheduler(() => this._onViewportChanged(), 250); - const scheduler = new RunOnceScheduler(() => { - const counterValue = ++this._modelChangeCounter; - if (this._currentFindCodeLensSymbolsPromise) { - this._currentFindCodeLensSymbolsPromise.cancel(); - } + const t1 = Date.now(); - this._currentFindCodeLensSymbolsPromise = createCancelablePromise(token => getCodeLensData(model, token)); + this._getCodeLensModelPromise?.cancel(); + this._getCodeLensModelPromise = createCancelablePromise(token => getCodeLensModel(model, token)); - this._currentFindCodeLensSymbolsPromise.then(result => { - if (counterValue === this._modelChangeCounter) { // only the last one wins - if (this._currentCodeLensModel) { - this._oldCodeLensModels.add(this._currentCodeLensModel); - } - this._currentCodeLensModel = result; - - // cache model to reduce flicker - this._codeLensCache.put(model, result); - - // render lenses - this._renderCodeLensSymbols(result); - detectVisibleLenses.schedule(); + this._getCodeLensModelPromise.then(result => { + if (this._currentCodeLensModel) { + this._oldCodeLensModels.add(this._currentCodeLensModel); } + this._currentCodeLensModel = result; + + // cache model to reduce flicker + this._codeLensCache.put(model, result); + + // update moving average + this._getCodeLensModelDelays.update(model, Date.now() - t1); + + // render lenses + this._renderCodeLensSymbols(result); + this._resolveCodeLensesInViewportSoon(); }, onUnexpectedError); - }, 250); + + }, this._getCodeLensModelDelays.get(model)); + this._localToDispose.add(scheduler); - this._localToDispose.add(detectVisibleLenses); + this._localToDispose.add(toDisposable(() => this._resolveCodeLensesScheduler.cancel())); this._localToDispose.add(this._editor.onDidChangeModelContent(() => { this._editor.changeDecorations(decorationsAccessor => { this._editor.changeViewZones(viewZonesAccessor => { @@ -209,17 +199,17 @@ export class CodeLensContribution implements IEditorContribution { }); // Compute new `visible` code lenses - detectVisibleLenses.schedule(); + this._resolveCodeLensesInViewportSoon(); // Ask for all references again scheduler.schedule(); })); this._localToDispose.add(this._editor.onDidScrollChange(e => { if (e.scrollTopChanged && this._lenses.length > 0) { - detectVisibleLenses.schedule(); + this._resolveCodeLensesInViewportSoon(); } })); this._localToDispose.add(this._editor.onDidLayoutChange(() => { - detectVisibleLenses.schedule(); + this._resolveCodeLensesInViewportSoon(); })); this._localToDispose.add(toDisposable(() => { if (this._editor.getModel()) { @@ -264,7 +254,7 @@ export class CodeLensContribution implements IEditorContribution { if (decChangeAccessor) { helper.commit(decChangeAccessor); } - this._lenses = []; + this._lenses.length = 0; } private _renderCodeLensSymbols(symbols: CodeLensModel): void { @@ -313,7 +303,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._resolveCodeLensesInViewportSoon())); codeLensIndex++; groupsIndex++; } @@ -327,7 +317,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._resolveCodeLensesInViewportSoon())); groupsIndex++; } @@ -338,11 +328,17 @@ export class CodeLensContribution implements IEditorContribution { scrollState.restore(this._editor); } - private _onViewportChanged(): void { - if (this._currentResolveCodeLensSymbolsPromise) { - this._currentResolveCodeLensSymbolsPromise.cancel(); - this._currentResolveCodeLensSymbolsPromise = undefined; + private _resolveCodeLensesInViewportSoon(): void { + const model = this._editor.getModel(); + if (model) { + this._resolveCodeLensesScheduler.schedule(this._resolveCodeLensesDelays.get(model)); } + } + + private _resolveCodeLensesInViewport(): void { + + this._resolveCodeLensesPromise?.cancel(); + this._resolveCodeLensesPromise = undefined; const model = this._editor.getModel(); if (!model) { @@ -363,6 +359,8 @@ export class CodeLensContribution implements IEditorContribution { return; } + const t1 = Date.now(); + const resolvePromise = createCancelablePromise(token => { const promises = toResolve.map((request, i) => { @@ -388,20 +386,24 @@ export class CodeLensContribution implements IEditorContribution { return Promise.all(promises); }); - this._currentResolveCodeLensSymbolsPromise = resolvePromise; + this._resolveCodeLensesPromise = resolvePromise; + + this._resolveCodeLensesPromise.then(() => { + + // update moving average + this._resolveCodeLensesDelays.update(model, Date.now() - t1); - this._currentResolveCodeLensSymbolsPromise.then(() => { if (this._currentCodeLensModel) { // update the cached state with new resolved items this._codeLensCache.put(model, this._currentCodeLensModel); } this._oldCodeLensModels.clear(); // dispose old models once we have updated the UI with the current model - if (resolvePromise === this._currentResolveCodeLensSymbolsPromise) { - this._currentResolveCodeLensSymbolsPromise = undefined; + if (resolvePromise === this._resolveCodeLensesPromise) { + this._resolveCodeLensesPromise = undefined; } }, err => { onUnexpectedError(err); // can also be cancellation! - if (resolvePromise === this._currentResolveCodeLensSymbolsPromise) { - this._currentResolveCodeLensSymbolsPromise = undefined; + if (resolvePromise === this._resolveCodeLensesPromise) { + this._resolveCodeLensesPromise = undefined; } }); } diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index d9e102ee50c..c85081502dd 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -14,8 +14,8 @@ import { ITextModel } from 'vs/editor/common/model'; import { DocumentSymbol, DocumentSymbolProvider, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; import { MarkerSeverity } from 'vs/platform/markers/common/markers'; import { Iterable } from 'vs/base/common/iterator'; -import { MovingAverage } from 'vs/base/common/numbers'; import { URI } from 'vs/base/common/uri'; +import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; export abstract class TreeElement { @@ -208,7 +208,7 @@ export class OutlineGroup extends TreeElement { export class OutlineModel extends TreeElement { - private static readonly _requestDurations = new LRUCache(50, 0.7); + private static readonly _requestDurations = new LanguageFeatureRequestDelays(DocumentSymbolProviderRegistry, 350); private static readonly _requests = new LRUCache, model: OutlineModel | undefined }>(9, 0.75); private static readonly _keys = new class { @@ -252,13 +252,7 @@ export class OutlineModel extends TreeElement { // keep moving average of request durations const now = Date.now(); data.promise.then(() => { - let key = this._keys.for(textModel, false); - let avg = this._requestDurations.get(key); - if (!avg) { - avg = new MovingAverage(); - this._requestDurations.set(key, avg); - } - avg.update(Date.now() - now); + this._requestDurations.update(textModel, Date.now() - now); }); } @@ -290,14 +284,7 @@ export class OutlineModel extends TreeElement { } static getRequestDelay(textModel: ITextModel | null): number { - if (!textModel) { - return 350; - } - const avg = this._requestDurations.get(this._keys.for(textModel, false)); - if (!avg) { - return 350; - } - return Math.max(350, Math.floor(1.3 * avg.value)); + return textModel ? this._requestDurations.get(textModel) : this._requestDurations.min; } private static _create(textModel: ITextModel, token: CancellationToken): Promise { diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index f5b6ec41d4f..66bdc31f06c 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -57,7 +57,7 @@ class MessageWidget { domNode.className = 'descriptioncontainer'; this._messageBlock = document.createElement('div'); - dom.addClass(this._messageBlock, 'message'); + this._messageBlock.classList.add('message'); this._messageBlock.setAttribute('aria-live', 'assertive'); this._messageBlock.setAttribute('role', 'alert'); domNode.appendChild(this._messageBlock); @@ -123,19 +123,19 @@ class MessageWidget { } if (source || code) { const detailsElement = document.createElement('span'); - dom.addClass(detailsElement, 'details'); + detailsElement.classList.add('details'); lastLineElement.appendChild(detailsElement); if (source) { const sourceElement = document.createElement('span'); sourceElement.innerText = source; - dom.addClass(sourceElement, 'source'); + sourceElement.classList.add('source'); detailsElement.appendChild(sourceElement); } if (code) { if (typeof code === 'string') { const codeElement = document.createElement('span'); codeElement.innerText = `(${code})`; - dom.addClass(codeElement, 'code'); + codeElement.classList.add('code'); detailsElement.appendChild(codeElement); } else { this._codeLink = dom.$('a.code-link'); @@ -166,7 +166,7 @@ class MessageWidget { let container = document.createElement('div'); let relatedResource = document.createElement('a'); - dom.addClass(relatedResource, 'filename'); + relatedResource.classList.add('filename'); relatedResource.innerText = `${getBaseLabel(related.resource)}(${related.startLineNumber}, ${related.startColumn}): `; relatedResource.title = getPathLabel(related.resource, undefined); this._relatedDiagnostics.set(relatedResource, related); @@ -318,7 +318,7 @@ export class MarkerNavigationWidget extends PeekViewWidget { protected _fillBody(container: HTMLElement): void { this._parentContainer = container; - dom.addClass(container, 'marker-widget'); + container.classList.add('marker-widget'); this._parentContainer.tabIndex = 0; this._parentContainer.setAttribute('role', 'tooltip'); diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts index f1bbff0aa81..b7942a37a72 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { dispose, DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IContextKey, IContextKeyService, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -64,8 +64,8 @@ export abstract class ReferencesController implements IEditorContribution { dispose(): void { this._referenceSearchVisible.reset(); this._disposables.dispose(); - dispose(this._widget); - dispose(this._model); + this._widget?.dispose(); + this._model?.dispose(); this._widget = undefined; this._model = undefined; } @@ -226,8 +226,8 @@ export abstract class ReferencesController implements IEditorContribution { } closeWidget(focusEditor = true): void { - dispose(this._widget); - dispose(this._model); + this._widget?.dispose(); + this._model?.dispose(); this._referenceSearchVisible.reset(); this._disposables.clear(); this._widget = undefined; diff --git a/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts b/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts index 1196c0293a7..915aae90522 100644 --- a/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts +++ b/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts @@ -55,8 +55,8 @@ class SymbolNavigationService implements ISymbolNavigationService { reset(): void { this._ctxHasSymbols.reset(); - dispose(this._currentState); - dispose(this._currentMessage); + this._currentState?.dispose(); + this._currentMessage?.dispose(); this._currentModel = undefined; this._currentIdx = -1; } @@ -138,7 +138,7 @@ class SymbolNavigationService implements ISymbolNavigationService { private _showMessage(): void { - dispose(this._currentMessage); + this._currentMessage?.dispose(); const kb = this._keybindingService.lookupKeybinding('editor.gotoNextSymbolFromResult'); const message = kb @@ -209,7 +209,7 @@ class EditorState { } private _onDidRemoveEditor(editor: ICodeEditor): void { - dispose(this._listener.get(editor)); + this._listener.get(editor)?.dispose(); this._listener.delete(editor); } } diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 1d7fb929b81..46cb4d7a542 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -240,11 +240,11 @@ export class ModesContentHoverWidget extends ContentHoverWidget { this._register(dom.addStandardDisposableListener(this.getDomNode(), dom.EventType.FOCUS, () => { if (this._colorPicker) { - dom.addClass(this.getDomNode(), 'colorpicker-hover'); + this.getDomNode().classList.add('colorpicker-hover'); } })); this._register(dom.addStandardDisposableListener(this.getDomNode(), dom.EventType.BLUR, () => { - dom.removeClass(this.getDomNode(), 'colorpicker-hover'); + this.getDomNode().classList.remove('colorpicker-hover'); })); this._register(editor.onDidChangeConfiguration((e) => { this._hoverOperation.setHoverTime(this._editor.getOption(EditorOption.hover).delay); diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index e94a981d167..6bec24fd00f 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -15,9 +15,10 @@ import { registerEditorContribution, EditorCommand, registerEditorCommand } from import { ICodeEditor, IContentWidget, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IPosition } from 'vs/editor/common/core/position'; -import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { inputValidationInfoBorder, inputValidationInfoBackground, inputValidationInfoForeground } from 'vs/platform/theme/common/colorRegistry'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; export class MessageController extends Disposable implements IEditorContribution { @@ -184,7 +185,7 @@ registerEditorContribution(MessageController.ID, MessageController); registerThemingParticipant((theme, collector) => { const border = theme.getColor(inputValidationInfoBorder); if (border) { - let borderWidth = theme.type === HIGH_CONTRAST ? 2 : 1; + let borderWidth = theme.type === ColorScheme.HIGH_CONTRAST ? 2 : 1; collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor { border-top-color: ${border}; }`); collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .message { border: ${borderWidth}px solid ${border}; }`); } diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index c34c98c2f97..9a3cf05bb24 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -20,11 +20,12 @@ import * as nls from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { editorHoverBackground, editorHoverBorder, textCodeBlockBackground, textLinkForeground, editorHoverForeground } from 'vs/platform/theme/common/colorRegistry'; -import { HIGH_CONTRAST, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ParameterHintsModel, TriggerContext } from 'vs/editor/contrib/parameterHints/parameterHintsModel'; import { pad } from 'vs/base/common/strings'; import { registerIcon, Codicon } from 'vs/base/common/codicons'; import { assertIsDefined } from 'vs/base/common/types'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; const $ = dom.$; @@ -364,7 +365,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { registerThemingParticipant((theme, collector) => { const border = theme.getColor(editorHoverBorder); if (border) { - const borderWidth = theme.type === HIGH_CONTRAST ? 2 : 1; + const borderWidth = theme.type === ColorScheme.HIGH_CONTRAST ? 2 : 1; collector.addRule(`.monaco-editor .parameter-hints-widget { border: ${borderWidth}px solid ${border}; }`); collector.addRule(`.monaco-editor .parameter-hints-widget.multiple .body { border-left: 1px solid ${border.transparent(0.5)}; }`); collector.addRule(`.monaco-editor .parameter-hints-widget .signature.has-docs { border-bottom: 1px solid ${border.transparent(0.5)}; }`); diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index c5e66c3b2f4..b6de17c9c99 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -18,7 +18,7 @@ import * as modes from 'vs/editor/common/modes'; import * as nls from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSelections'; import { BracketSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/bracketSelections'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -64,7 +64,7 @@ class SmartSelectController implements IEditorContribution { } dispose(): void { - dispose(this._selectionListener); + this._selectionListener?.dispose(); } run(forward: boolean): Promise | void { @@ -106,10 +106,10 @@ class SmartSelectController implements IEditorContribution { this._state = ranges.map(ranges => new SelectionRanges(0, ranges)); // listen to caret move and forget about state - dispose(this._selectionListener); + this._selectionListener?.dispose(); this._selectionListener = this._editor.onDidChangeCursorPosition(() => { if (!this._ignoreSelection) { - dispose(this._selectionListener); + this._selectionListener?.dispose(); this._state = undefined; } }); diff --git a/src/vs/editor/contrib/snippet/snippetController2.ts b/src/vs/editor/contrib/snippet/snippetController2.ts index 7c35372a614..6459f4402f2 100644 --- a/src/vs/editor/contrib/snippet/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/snippetController2.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { dispose, DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorCommand, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; @@ -75,7 +75,7 @@ export class SnippetController2 implements IEditorContribution { this._inSnippet.reset(); this._hasPrevTabstop.reset(); this._hasNextTabstop.reset(); - dispose(this._session); + this._session?.dispose(); this._snippetListener.dispose(); } @@ -211,7 +211,7 @@ export class SnippetController2 implements IEditorContribution { this._hasPrevTabstop.reset(); this._hasNextTabstop.reset(); this._snippetListener.clear(); - dispose(this._session); + this._session?.dispose(); this._session = undefined; this._modelVersionId = -1; if (resetSelection) { diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index be783ef5177..d7fb21e915b 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -351,6 +351,7 @@ box-sizing: border-box; height: 100%; width: 100%; + padding-right: 22px; } .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .type { diff --git a/src/vs/editor/contrib/suggest/suggestAlternatives.ts b/src/vs/editor/contrib/suggest/suggestAlternatives.ts index 6c9a148e618..7c72d74d4f3 100644 --- a/src/vs/editor/contrib/suggest/suggestAlternatives.ts +++ b/src/vs/editor/contrib/suggest/suggestAlternatives.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { CompletionModel } from './completionModel'; @@ -34,7 +34,7 @@ export class SuggestAlternatives { reset(): void { this._ckOtherSuggestions.reset(); - dispose(this._listener); + this._listener?.dispose(); this._model = undefined; this._acceptNext = undefined; this._ignore = false; diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 3b92a21d7cc..9478f92d212 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -598,7 +598,8 @@ export class TriggerSuggestAction extends EditorAction { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.Space, - mac: { primary: KeyMod.WinCtrl | KeyCode.Space, secondary: [KeyMod.Alt | KeyCode.Escape] }, + secondary: [KeyMod.CtrlCmd | KeyCode.KEY_I], + mac: { primary: KeyMod.WinCtrl | KeyCode.Space, secondary: [KeyMod.Alt | KeyCode.Escape, KeyMod.CtrlCmd | KeyCode.KEY_I] }, weight: KeybindingWeight.EditorContrib } }); diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index bb4f269ece8..718471864de 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -436,7 +436,7 @@ export class SuggestModel implements IDisposable { Promise.all([completions, wordDistance]).then(async ([completions, wordDistance]) => { - dispose(this._requestToken); + this._requestToken?.dispose(); if (this._state === State.Idle) { return; diff --git a/src/vs/editor/contrib/suggest/wordContextKey.ts b/src/vs/editor/contrib/suggest/wordContextKey.ts index a8f3a87cfe8..a9558304bac 100644 --- a/src/vs/editor/contrib/suggest/wordContextKey.ts +++ b/src/vs/editor/contrib/suggest/wordContextKey.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -29,7 +29,7 @@ export class WordContextKey extends Disposable { dispose(): void { super.dispose(); - dispose(this._selectionListener); + this._selectionListener?.dispose(); this._ckAtEnd.reset(); } diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts index 31108f99ddb..192f9218551 100644 --- a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts @@ -20,8 +20,9 @@ import { NULL_STATE, nullTokenize, nullTokenize2 } from 'vs/editor/common/modes/ import { IModeService } from 'vs/editor/common/services/modeService'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; import { editorHoverBackground, editorHoverBorder, editorHoverForeground } from 'vs/platform/theme/common/colorRegistry'; -import { HIGH_CONTRAST, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { InspectTokensNLS } from 'vs/editor/common/standaloneStrings'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; class InspectTokensController extends Disposable implements IEditorContribution { @@ -332,7 +333,7 @@ registerEditorAction(InspectTokens); registerThemingParticipant((theme, collector) => { const border = theme.getColor(editorHoverBorder); if (border) { - let borderWidth = theme.type === HIGH_CONTRAST ? 2 : 1; + let borderWidth = theme.type === ColorScheme.HIGH_CONTRAST ? 2 : 1; collector.addRule(`.monaco-editor .tokens-inspect-widget { border: ${borderWidth}px solid ${border}; }`); collector.addRule(`.monaco-editor .tokens-inspect-widget .tokens-inspect-separator { background-color: ${border}; }`); } diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 9e3f3ac0c9c..71d419567ee 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -15,6 +15,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { ColorIdentifier, Extensions, IColorRegistry } from 'vs/platform/theme/common/colorRegistry'; import { Extensions as ThemingExtensions, ICssStyleCollector, IFileIconTheme, IThemingRegistry, ITokenStyle } from 'vs/platform/theme/common/themeService'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; const VS_THEME_NAME = 'vs'; const VS_DARK_THEME_NAME = 'vs-dark'; @@ -107,11 +108,11 @@ class StandaloneTheme implements IStandaloneTheme { return Object.prototype.hasOwnProperty.call(this.getColors(), colorId); } - public get type() { + public get type(): ColorScheme { switch (this.base) { - case VS_THEME_NAME: return 'light'; - case HC_BLACK_THEME_NAME: return 'hc'; - default: return 'dark'; + case VS_THEME_NAME: return ColorScheme.LIGHT; + case HC_BLACK_THEME_NAME: return ColorScheme.HIGH_CONTRAST; + default: return ColorScheme.DARK; } } diff --git a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts index 282422dcbd2..e3c7a31f160 100644 --- a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts +++ b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts @@ -12,7 +12,8 @@ import { TokenTheme } from 'vs/editor/common/modes/supports/tokenization'; import { ILineTokens, IToken, TokenizationSupport2Adapter, TokensProvider } from 'vs/editor/standalone/browser/standaloneLanguages'; import { IStandaloneTheme, IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; -import { IFileIconTheme, IColorTheme, LIGHT, ITokenStyle } from 'vs/platform/theme/common/themeService'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { IFileIconTheme, IColorTheme, ITokenStyle } from 'vs/platform/theme/common/themeService'; suite('TokenizationSupport2Adapter', () => { @@ -46,9 +47,9 @@ suite('TokenizationSupport2Adapter', () => { tokenTheme: new MockTokenTheme(), - themeName: LIGHT, + themeName: ColorScheme.LIGHT, - type: LIGHT, + type: ColorScheme.LIGHT, getColor: (color: ColorIdentifier, useDefault?: boolean): Color => { throw new Error('Not implemented'); diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index b323b460034..98467412ab0 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -42,7 +42,7 @@ export class BackupMainService implements IBackupMainService { @IConfigurationService private readonly configurationService: IConfigurationService, @ILogService private readonly logService: ILogService ) { - this.backupHome = environmentService.backupHome.fsPath; + this.backupHome = environmentService.backupHome; this.workspacesJsonPath = environmentService.backupWorkspacesPath; } diff --git a/src/vs/platform/electron/common/electron.ts b/src/vs/platform/electron/common/electron.ts index 80a07026191..90033410206 100644 --- a/src/vs/platform/electron/common/electron.ts +++ b/src/vs/platform/electron/common/electron.ts @@ -5,9 +5,10 @@ import { Event } from 'vs/base/common/event'; import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue, MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; -import { IOpenedWindow, IWindowOpenable, IOpenEmptyWindowOptions, IOpenWindowOptions, ColorScheme } from 'vs/platform/windows/common/windows'; +import { IOpenedWindow, IWindowOpenable, IOpenEmptyWindowOptions, IOpenWindowOptions } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; export interface IOSProperties { type: string; diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts index 4088e35ff58..acde70fb201 100644 --- a/src/vs/platform/electron/electron-main/electronMainService.ts +++ b/src/vs/platform/electron/electron-main/electronMainService.ts @@ -8,7 +8,7 @@ import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-m import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, Menu, BrowserWindow, app, clipboard, powerMonitor, nativeTheme } from 'electron'; import { OpenContext } from 'vs/platform/windows/node/window'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { IOpenedWindow, IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions, ColorScheme } from 'vs/platform/windows/common/windows'; +import { IOpenedWindow, IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { isMacintosh, isWindows, isRootUser } from 'vs/base/common/platform'; import { ICommonElectronService, IOSProperties } from 'vs/platform/electron/common/electron'; @@ -22,6 +22,7 @@ import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; import { arch, totalmem, release, platform, type } from 'os'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; export interface IElectronMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } @@ -45,11 +46,13 @@ export class ElectronMainService implements IElectronMainService { // Color Scheme changes nativeTheme.on('updated', () => { - let colorScheme = ColorScheme.DEFAULT; + let colorScheme: ColorScheme; if (nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors) { colorScheme = ColorScheme.HIGH_CONTRAST; } else if (nativeTheme.shouldUseDarkColors) { colorScheme = ColorScheme.DARK; + } else { + colorScheme = ColorScheme.LIGHT; } this._onColorSchemeChange.fire(colorScheme); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index acd11a3d65d..8ce5820075f 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -18,8 +18,6 @@ export interface IExtensionHostDebugParams extends IDebugParams { debugId?: string; } -export const BACKUPS = 'Backups'; - /** * A basic environment service that can be used in various processes, * such as main, renderer and shared process. Use subclasses of this @@ -44,7 +42,6 @@ export interface IEnvironmentService { snippetsHome: URI; // --- data paths - backupHome: URI; untitledWorkspacesHome: URI; globalStorageHome: URI; @@ -100,6 +97,7 @@ export interface INativeEnvironmentService extends IEnvironmentService { appSettingsHome: URI; userDataPath: string; machineSettingsResource: URI; + backupHome: string; backupWorkspacesPath: string; nodeCachedDataDir?: string; installSourcePath: string; diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index ef8dc5d2dd5..01459254fa0 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDebugParams, IExtensionHostDebugParams, BACKUPS, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import * as crypto from 'crypto'; import * as paths from 'vs/base/node/paths'; @@ -94,10 +94,10 @@ export class EnvironmentService implements INativeEnvironmentService { get isExtensionDevelopment(): boolean { return !!this._args.extensionDevelopmentPath; } @memoize - get backupHome(): URI { return URI.file(path.join(this.userDataPath, BACKUPS)); } + get backupHome(): string { return path.join(this.userDataPath, 'Backups'); } @memoize - get backupWorkspacesPath(): string { return path.join(this.backupHome.fsPath, 'workspaces.json'); } + get backupWorkspacesPath(): string { return path.join(this.backupHome, 'workspaces.json'); } @memoize get untitledWorkspacesHome(): URI { return URI.file(path.join(this.userDataPath, 'Workspaces')); } diff --git a/src/vs/platform/extensionManagement/common/configRemotes.ts b/src/vs/platform/extensionManagement/common/configRemotes.ts index b4d631be3f0..4db44fa4abf 100644 --- a/src/vs/platform/extensionManagement/common/configRemotes.ts +++ b/src/vs/platform/extensionManagement/common/configRemotes.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { endsWith } from 'vs/base/common/strings'; const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/; const SshUrlMatcher = /^([^@:]+@)?([^:]+):(.+)$/; @@ -76,7 +75,7 @@ function stripPort(authority: string): string | null { function normalizeRemote(host: string | null, path: string, stripEndingDotGit: boolean): string | null { if (host && path) { - if (stripEndingDotGit && endsWith(path, '.git')) { + if (stripEndingDotGit && path.endsWith('.git')) { path = path.substr(0, path.length - 4); } return (path.indexOf('/') === 0) ? `${host}${path}` : `${host}/${path}`; diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 955557b345a..e352246246d 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -6,7 +6,7 @@ import { getErrorMessage, isPromiseCanceledError, canceled } from 'vs/base/common/errors'; import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionIdentifier, IReportedExtension, InstallOperation, ITranslation, IGalleryExtensionVersion, IGalleryExtensionAssets, isIExtensionIdentifier, DefaultIconPath } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId, getGalleryExtensionTelemetryData, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { assign, getOrDefault } from 'vs/base/common/objects'; +import { getOrDefault } from 'vs/base/common/objects'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IPager } from 'vs/base/common/paging'; import { IRequestService, asJson, asText } from 'vs/platform/request/common/request'; @@ -158,7 +158,7 @@ class Query { get flags(): number { return this.state.flags; } withPage(pageNumber: number, pageSize: number = this.state.pageSize): Query { - return new Query(assign({}, this.state, { pageNumber, pageSize })); + return new Query({ ...this.state, pageNumber, pageSize }); } withFilter(filterType: FilterType, ...values: string[]): Query { @@ -167,23 +167,23 @@ class Query { ...values.length ? values.map(value => ({ filterType, value })) : [{ filterType }] ]; - return new Query(assign({}, this.state, { criteria })); + return new Query({ ...this.state, criteria }); } withSortBy(sortBy: SortBy): Query { - return new Query(assign({}, this.state, { sortBy })); + return new Query({ ...this.state, sortBy }); } withSortOrder(sortOrder: SortOrder): Query { - return new Query(assign({}, this.state, { sortOrder })); + return new Query({ ...this.state, sortOrder }); } withFlags(...flags: Flags[]): Query { - return new Query(assign({}, this.state, { flags: flags.reduce((r, f) => r | f, 0) })); + return new Query({ ...this.state, flags: flags.reduce((r, f) => r | f, 0) }); } withAssetTypes(...assetTypes: string[]): Query { - return new Query(assign({}, this.state, { assetTypes })); + return new Query({ ...this.state, assetTypes }); } get raw(): any { @@ -381,10 +381,10 @@ export class ExtensionGalleryService implements IExtensionGalleryService { } } - private getCompatibleExtensionByEngine(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise { + private async getCompatibleExtensionByEngine(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise { const extension: IGalleryExtension | null = isIExtensionIdentifier(arg1) ? null : arg1; if (extension && extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version)) { - return Promise.resolve(extension); + return extension; } const { id, uuid } = extension ? extension.identifier : arg1; let query = new Query() @@ -398,40 +398,38 @@ export class ExtensionGalleryService implements IExtensionGalleryService { query = query.withFilter(FilterType.ExtensionName, id); } - return this.queryGallery(query, CancellationToken.None) - .then(({ galleryExtensions }) => { - const [rawExtension] = galleryExtensions; - if (!rawExtension || !rawExtension.versions.length) { - return null; + const { galleryExtensions } = await this.queryGallery(query, CancellationToken.None); + const [rawExtension] = galleryExtensions; + if (!rawExtension || !rawExtension.versions.length) { + return null; + } + + if (version) { + const versionAsset = rawExtension.versions.filter(v => v.version === version)[0]; + if (versionAsset) { + const extension = toExtension(rawExtension, versionAsset, 0, query); + if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version)) { + return extension; } - if (version) { - const versionAsset = rawExtension.versions.filter(v => v.version === version)[0]; - if (versionAsset) { - const extension = toExtension(rawExtension, versionAsset, 0, query); - if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version)) { - return extension; - } - } - return null; - } - return this.getLastValidExtensionVersion(rawExtension, rawExtension.versions) - .then(rawVersion => { - if (rawVersion) { - return toExtension(rawExtension, rawVersion, 0, query); - } - return null; - }); - }); + } + return null; + } + + const rawVersion = await this.getLastValidExtensionVersion(rawExtension, rawExtension.versions); + if (rawVersion) { + return toExtension(rawExtension, rawVersion, 0, query); + } + return null; } query(token: CancellationToken): Promise>; query(options: IQueryOptions, token: CancellationToken): Promise>; - query(arg1: any, arg2?: any): Promise> { + async query(arg1: any, arg2?: any): Promise> { const options: IQueryOptions = CancellationToken.isCancellationToken(arg1) ? {} : arg1; const token: CancellationToken = CancellationToken.isCancellationToken(arg1) ? arg1 : arg2; if (!this.isEnabled()) { - return Promise.reject(new Error('No extension gallery service configured.')); + throw new Error('No extension gallery service configured.'); } const type = options.names ? 'ids' : (options.text ? 'text' : 'all'); @@ -496,84 +494,80 @@ export class ExtensionGalleryService implements IExtensionGalleryService { query = query.withSortOrder(options.sortOrder); } - return this.queryGallery(query, token).then(({ galleryExtensions, total }) => { - const extensions = galleryExtensions.map((e, index) => toExtension(e, e.versions[0], index, query, options.source)); - const pageSize = query.pageSize; - const getPage = (pageIndex: number, ct: CancellationToken) => { - if (ct.isCancellationRequested) { - return Promise.reject(canceled()); - } + const { galleryExtensions, total } = await this.queryGallery(query, token); + const extensions = galleryExtensions.map((e, index) => toExtension(e, e.versions[0], index, query, options.source)); + const getPage = async (pageIndex: number, ct: CancellationToken) => { + if (ct.isCancellationRequested) { + throw canceled(); + } + const nextPageQuery = query.withPage(pageIndex + 1); + const { galleryExtensions } = await this.queryGallery(nextPageQuery, ct); + return galleryExtensions.map((e, index) => toExtension(e, e.versions[0], index, nextPageQuery, options.source)); + }; - const nextPageQuery = query.withPage(pageIndex + 1); - return this.queryGallery(nextPageQuery, ct) - .then(({ galleryExtensions }) => galleryExtensions.map((e, index) => toExtension(e, e.versions[0], index, nextPageQuery, options.source))); - }; - - return { firstPage: extensions, total, pageSize, getPage } as IPager; - }); + return { firstPage: extensions, total, pageSize: query.pageSize, getPage } as IPager; } - private queryGallery(query: Query, token: CancellationToken): Promise<{ galleryExtensions: IRawGalleryExtension[], total: number; }> { + private async queryGallery(query: Query, token: CancellationToken): Promise<{ galleryExtensions: IRawGalleryExtension[], total: number; }> { + if (!this.isEnabled()) { + throw new Error('No extension gallery service configured.'); + } + // Always exclude non validated and unpublished extensions query = query .withFlags(query.flags, Flags.ExcludeNonValidated) .withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished)); - if (!this.isEnabled()) { - return Promise.reject(new Error('No extension gallery service configured.')); + const commonHeaders = await this.commonHeadersPromise; + const data = JSON.stringify(query.raw); + const headers = { + ...commonHeaders, + 'Content-Type': 'application/json', + 'Accept': 'application/json;api-version=3.0-preview.1', + 'Accept-Encoding': 'gzip', + 'Content-Length': String(data.length) + }; + + const context = await this.requestService.request({ + type: 'POST', + url: this.api('/extensionquery'), + data, + headers + }, token); + + if (context.res.statusCode && context.res.statusCode >= 400 && context.res.statusCode < 500) { + return { galleryExtensions: [], total: 0 }; } - return this.commonHeadersPromise.then(commonHeaders => { - const data = JSON.stringify(query.raw); - const headers = assign({}, commonHeaders, { - 'Content-Type': 'application/json', - 'Accept': 'application/json;api-version=3.0-preview.1', - 'Accept-Encoding': 'gzip', - 'Content-Length': data.length - }); - return this.requestService.request({ - type: 'POST', - url: this.api('/extensionquery'), - data, - headers - }, token).then(context => { + const result = await asJson(context); + if (result) { + const r = result.results[0]; + const galleryExtensions = r.extensions; + const resultCount = r.resultMetadata && r.resultMetadata.filter(m => m.metadataType === 'ResultCount')[0]; + const total = resultCount && resultCount.metadataItems.filter(i => i.name === 'TotalCount')[0].count || 0; - if (context.res.statusCode && context.res.statusCode >= 400 && context.res.statusCode < 500) { - return { galleryExtensions: [], total: 0 }; - } - - return asJson(context).then(result => { - if (result) { - const r = result.results[0]; - const galleryExtensions = r.extensions; - const resultCount = r.resultMetadata && r.resultMetadata.filter(m => m.metadataType === 'ResultCount')[0]; - const total = resultCount && resultCount.metadataItems.filter(i => i.name === 'TotalCount')[0].count || 0; - - return { galleryExtensions, total }; - } - return { galleryExtensions: [], total: 0 }; - }); - }); - }); + return { galleryExtensions, total }; + } + return { galleryExtensions: [], total: 0 }; } - reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise { + async reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise { if (!this.isEnabled()) { - return Promise.resolve(undefined); + return undefined; } - return this.commonHeadersPromise.then(commonHeaders => { - const headers = { ...commonHeaders, Accept: '*/*;api-version=4.0-preview.1' }; - - return this.requestService.request({ + const commonHeaders = await this.commonHeadersPromise; + const headers = { ...commonHeaders, Accept: '*/*;api-version=4.0-preview.1' }; + try { + await this.requestService.request({ type: 'POST', url: this.api(`/publishers/${publisher}/extensions/${name}/${version}/stats?statType=${type}`), headers - }, CancellationToken.None).then(undefined, () => undefined); - }); + }, CancellationToken.None); + } catch (error) { /* Ignore */ } } - download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise { + async download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise { this.logService.trace('ExtensionGalleryService#download', extension.identifier.id); const data = getGalleryExtensionTelemetryData(extension); const startTime = new Date().getTime(); @@ -585,7 +579,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { ] } */ - const log = (duration: number) => this.telemetryService.publicLog('galleryService:downloadVSIX', assign(data, { duration })); + const log = (duration: number) => this.telemetryService.publicLog('galleryService:downloadVSIX', { ...data, duration }); const operationParam = operation === InstallOperation.Install ? 'install' : operation === InstallOperation.Update ? 'update' : ''; const downloadAsset = operationParam ? { @@ -593,46 +587,46 @@ export class ExtensionGalleryService implements IExtensionGalleryService { fallbackUri: `${extension.assets.download.fallbackUri}?${operationParam}=true` } : extension.assets.download; - return this.getAsset(downloadAsset) - .then(context => this.fileService.writeFile(location, context.stream)) - .then(() => log(new Date().getTime() - startTime)); + const context = await this.getAsset(downloadAsset); + await this.fileService.writeFile(location, context.stream); + log(new Date().getTime() - startTime); } - getReadme(extension: IGalleryExtension, token: CancellationToken): Promise { + async getReadme(extension: IGalleryExtension, token: CancellationToken): Promise { if (extension.assets.readme) { - return this.getAsset(extension.assets.readme, {}, token) - .then(context => asText(context)) - .then(content => content || ''); + const context = await this.getAsset(extension.assets.readme, {}, token); + const content = await asText(context); + return content || ''; } - return Promise.resolve(''); + return ''; } - getManifest(extension: IGalleryExtension, token: CancellationToken): Promise { + async getManifest(extension: IGalleryExtension, token: CancellationToken): Promise { if (extension.assets.manifest) { - return this.getAsset(extension.assets.manifest, {}, token) - .then(asText) - .then(text => text ? JSON.parse(text) : null); + const context = await this.getAsset(extension.assets.manifest, {}, token); + const text = await asText(context); + return text ? JSON.parse(text) : null; } - return Promise.resolve(null); + return null; } - getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise { + async getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise { const asset = extension.assets.coreTranslations.filter(t => t[0] === languageId.toUpperCase())[0]; if (asset) { - return this.getAsset(asset[1]) - .then(asText) - .then(text => text ? JSON.parse(text) : null); + const context = await this.getAsset(asset[1]); + const text = await asText(context); + return text ? JSON.parse(text) : null; } - return Promise.resolve(null); + return null; } - getChangelog(extension: IGalleryExtension, token: CancellationToken): Promise { + async getChangelog(extension: IGalleryExtension, token: CancellationToken): Promise { if (extension.assets.changelog) { - return this.getAsset(extension.assets.changelog, {}, token) - .then(context => asText(context)) - .then(content => content || ''); + const context = await this.getAsset(extension.assets.changelog, {}, token); + const content = await asText(context); + return content || ''; } - return Promise.resolve(''); + return ''; } async getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise { @@ -667,48 +661,45 @@ export class ExtensionGalleryService implements IExtensionGalleryService { return result; } - private getAsset(asset: IGalleryExtensionAsset, options: IRequestOptions = {}, token: CancellationToken = CancellationToken.None): Promise { - return this.commonHeadersPromise.then(commonHeaders => { - const baseOptions = { type: 'GET' }; - const headers = assign({}, commonHeaders, options.headers || {}); - options = assign({}, options, baseOptions, { headers }); + private async getAsset(asset: IGalleryExtensionAsset, options: IRequestOptions = {}, token: CancellationToken = CancellationToken.None): Promise { + const commonHeaders = await this.commonHeadersPromise; + const baseOptions = { type: 'GET' }; + const headers = { ...commonHeaders, ...(options.headers || {}) }; + options = { ...options, ...baseOptions, headers }; - const url = asset.uri; - const fallbackUrl = asset.fallbackUri; - const firstOptions = assign({}, options, { url }); + const url = asset.uri; + const fallbackUrl = asset.fallbackUri; + const firstOptions = { ...options, url }; - return this.requestService.request(firstOptions, token) - .then(context => { - if (context.res.statusCode === 200) { - return Promise.resolve(context); - } + try { + const context = await this.requestService.request(firstOptions, token); + if (context.res.statusCode === 200) { + return context; + } + const message = await asText(context); + throw new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`); + } catch (err) { + if (isPromiseCanceledError(err)) { + throw err; + } - return asText(context) - .then(message => Promise.reject(new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`))); - }) - .then(undefined, err => { - if (isPromiseCanceledError(err)) { - return Promise.reject(err); - } + const message = getErrorMessage(err); + type GalleryServiceCDNFallbackClassification = { + url: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + message: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + }; + type GalleryServiceCDNFallbackEvent = { + url: string; + message: string; + }; + this.telemetryService.publicLog2('galleryService:cdnFallback', { url, message }); - const message = getErrorMessage(err); - type GalleryServiceCDNFallbackClassification = { - url: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - message: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - }; - type GalleryServiceCDNFallbackEvent = { - url: string; - message: string; - }; - this.telemetryService.publicLog2('galleryService:cdnFallback', { url, message }); - - const fallbackOptions = assign({}, options, { url: fallbackUrl }); - return this.requestService.request(fallbackOptions, token); - }); - }); + const fallbackOptions = { ...options, url: fallbackUrl }; + return this.requestService.request(fallbackOptions, token); + } } - private getLastValidExtensionVersion(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise { + private async getLastValidExtensionVersion(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise { const version = this.getLastValidExtensionVersionFromProperties(extension, versions); if (version) { return version; @@ -716,82 +707,82 @@ export class ExtensionGalleryService implements IExtensionGalleryService { return this.getLastValidExtensionVersionRecursively(extension, versions); } - private getLastValidExtensionVersionFromProperties(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise | null { + private getLastValidExtensionVersionFromProperties(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): IRawGalleryExtensionVersion | null { for (const version of versions) { const engine = getEngine(version); if (!engine) { return null; } if (isEngineValid(engine, this.productService.version)) { - return Promise.resolve(version); + return version; } } return null; } - private getEngine(version: IRawGalleryExtensionVersion): Promise { + private async getEngine(version: IRawGalleryExtensionVersion): Promise { const engine = getEngine(version); if (engine) { - return Promise.resolve(engine); + return engine; } - const manifest = getVersionAsset(version, AssetType.Manifest); - if (!manifest) { - return Promise.reject('Manifest was not found'); + const manifestAsset = getVersionAsset(version, AssetType.Manifest); + if (!manifestAsset) { + throw new Error('Manifest was not found'); } const headers = { 'Accept-Encoding': 'gzip' }; - return this.getAsset(manifest, { headers }) - .then(context => asJson(context)) - .then(manifest => manifest ? manifest.engines.vscode : Promise.reject('Error while reading manifest')); + const context = await this.getAsset(manifestAsset, { headers }); + const manifest = await asJson(context); + if (manifest) { + return manifest.engines.vscode; + } + + throw new Error('Error while reading manifest'); } - private getLastValidExtensionVersionRecursively(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise { + private async getLastValidExtensionVersionRecursively(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise { if (!versions.length) { - return Promise.resolve(null); + return null; } const version = versions[0]; - return this.getEngine(version) - .then(engine => { - if (!isEngineValid(engine, this.productService.version)) { - return this.getLastValidExtensionVersionRecursively(extension, versions.slice(1)); - } + const engine = await this.getEngine(version); + if (!isEngineValid(engine, this.productService.version)) { + return this.getLastValidExtensionVersionRecursively(extension, versions.slice(1)); + } - version.properties = version.properties || []; - version.properties.push({ key: PropertyType.Engine, value: engine }); - return version; - }); + version.properties = version.properties || []; + version.properties.push({ key: PropertyType.Engine, value: engine }); + return version; } - getExtensionsReport(): Promise { + async getExtensionsReport(): Promise { if (!this.isEnabled()) { - return Promise.reject(new Error('No extension gallery service configured.')); + throw new Error('No extension gallery service configured.'); } if (!this.extensionsControlUrl) { - return Promise.resolve([]); + return []; } - return this.requestService.request({ type: 'GET', url: this.extensionsControlUrl }, CancellationToken.None).then(context => { - if (context.res.statusCode !== 200) { - return Promise.reject(new Error('Could not get extensions report.')); + const context = await this.requestService.request({ type: 'GET', url: this.extensionsControlUrl }, CancellationToken.None); + if (context.res.statusCode !== 200) { + throw new Error('Could not get extensions report.'); + } + + const result = await asJson(context); + const map = new Map(); + + if (result) { + for (const id of result.malicious) { + const ext = map.get(id) || { id: { id }, malicious: true, slow: false }; + ext.malicious = true; + map.set(id, ext); } + } - return asJson(context).then(result => { - const map = new Map(); - - if (result) { - for (const id of result.malicious) { - const ext = map.get(id) || { id: { id }, malicious: true, slow: false }; - ext.malicious = true; - map.set(id, ext); - } - } - - return [...map.values()]; - }); - }); + return [...map.values()]; } } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index b26cf4129b2..9b0fd4db47e 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; import * as pfs from 'vs/base/node/pfs'; -import { assign } from 'vs/base/common/objects'; import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { zip, IFile } from 'vs/base/node/zip'; @@ -46,6 +45,7 @@ import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common import { ExtensionsDownloader } from 'vs/platform/extensionManagement/node/extensionDownloader'; import { ExtensionsScanner, IMetadata } from 'vs/platform/extensionManagement/node/extensionsScanner'; import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle'; +import { IStringDictionary } from 'vs/base/common/collections'; const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled'; const INSTALL_ERROR_DOWNLOADING = 'downloading'; @@ -677,7 +677,10 @@ export class ExtensionManagementService extends Disposable implements IExtension private setUninstalled(...extensions: ILocalExtension[]): Promise<{ [id: string]: boolean }> { const ids: ExtensionIdentifierWithVersion[] = extensions.map(e => new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version)); - return this.extensionsScanner.withUninstalledExtensions(uninstalled => assign(uninstalled, ids.reduce((result, id) => { result[id.key()] = true; return result; }, {} as { [id: string]: boolean }))); + return this.extensionsScanner.withUninstalledExtensions(uninstalled => { + const newUninstalled = (ids.reduce>((result, id) => { result[id.key()] = true; return result; }, {})); + return { ...uninstalled, ...newUninstalled }; + }); } private unsetUninstalled(extensionIdentifier: ExtensionIdentifierWithVersion): Promise { @@ -745,6 +748,6 @@ export class ExtensionManagementService extends Disposable implements IExtension ] } */ - this.telemetryService.publicLogError(eventName, assign(extensionData, { success: !error, duration, errorcode })); + this.telemetryService.publicLogError(eventName, { ...extensionData, success: !error, duration, errorcode }); } } diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts index ef3ba9f7ace..819dc9d2bd2 100644 --- a/src/vs/platform/extensionManagement/node/extensionsScanner.ts +++ b/src/vs/platform/extensionManagement/node/extensionsScanner.ts @@ -22,7 +22,7 @@ import { CancellationToken } from 'vscode'; import { extract, ExtractError } from 'vs/base/node/zip'; import { isWindows } from 'vs/base/common/platform'; import { flatten } from 'vs/base/common/arrays'; -import { assign } from 'vs/base/common/objects'; +import { IStringDictionary } from 'vs/base/common/collections'; const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; @@ -31,6 +31,7 @@ const INSTALL_ERROR_DELETING = 'deleting'; const INSTALL_ERROR_RENAMING = 'renaming'; export type IMetadata = Partial; +type ILocalExtensionManifest = IExtensionManifest & { __metadata?: IMetadata }; export class ExtensionsScanner extends Disposable { @@ -133,7 +134,7 @@ export class ExtensionsScanner extends Disposable { const manifestPath = path.join(local.location.fsPath, 'package.json'); const raw = await pfs.readFile(manifestPath, 'utf8'); const { manifest } = await this.parseManifest(raw); - assign(manifest, { __metadata: metadata }); + (manifest as ILocalExtensionManifest).__metadata = metadata; await pfs.writeFile(manifestPath, JSON.stringify(manifest, null, '\t')); return local; } @@ -142,7 +143,7 @@ export class ExtensionsScanner extends Disposable { return this.withUninstalledExtensions(uninstalled => uninstalled); } - async withUninstalledExtensions(fn: (uninstalled: { [id: string]: boolean; }) => T): Promise { + async withUninstalledExtensions(fn: (uninstalled: IStringDictionary) => T): Promise { return this.uninstalledFileLimiter.queue(async () => { let result: T | null = null; return pfs.readFile(this.uninstalledPath, 'utf8') diff --git a/src/vs/platform/request/electron-main/requestMainService.ts b/src/vs/platform/request/electron-main/requestMainService.ts index 7f8b7141212..d08f82f4510 100644 --- a/src/vs/platform/request/electron-main/requestMainService.ts +++ b/src/vs/platform/request/electron-main/requestMainService.ts @@ -5,7 +5,6 @@ import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; import { RequestService as NodeRequestService, IRawRequestFunction } from 'vs/platform/request/node/requestService'; -import { assign } from 'vs/base/common/objects'; import { net } from 'electron'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -16,6 +15,6 @@ function getRawRequest(options: IRequestOptions): IRawRequestFunction { export class RequestMainService extends NodeRequestService { request(options: IRequestOptions, token: CancellationToken): Promise { - return super.request(assign({}, options || {}, { getRawRequest }), token); + return super.request({ ...(options || {}), getRawRequest }, token); } } diff --git a/src/vs/platform/request/node/requestService.ts b/src/vs/platform/request/node/requestService.ts index 377929d065d..41e23aea2f3 100644 --- a/src/vs/platform/request/node/requestService.ts +++ b/src/vs/platform/request/node/requestService.ts @@ -9,7 +9,6 @@ import * as streams from 'vs/base/common/stream'; import { createGunzip } from 'zlib'; import { parse as parseUrl } from 'url'; import { Disposable } from 'vs/base/common/lifecycle'; -import { assign } from 'vs/base/common/objects'; import { isBoolean, isNumber } from 'vs/base/common/types'; import { canceled } from 'vs/base/common/errors'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -67,7 +66,10 @@ export class RequestService extends Disposable implements IRequestService { options.strictSSL = strictSSL; if (this.authorization) { - options.headers = assign(options.headers || {}, { 'Proxy-Authorization': this.authorization }); + options.headers = { + ...(options.headers || {}), + 'Proxy-Authorization': this.authorization + }; } return this._request(options, token); @@ -107,10 +109,11 @@ export class RequestService extends Disposable implements IRequestService { req = rawRequest(opts, (res: http.IncomingMessage) => { const followRedirects: number = isNumber(options.followRedirects) ? options.followRedirects : 3; if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) { - this._request(assign({}, options, { + this._request({ + ...options, url: res.headers['location'], followRedirects: followRedirects - 1 - }), token).then(c, e); + }, token).then(c, e); } else { let stream: streams.ReadableStreamEvents = res; diff --git a/src/vs/platform/theme/common/theme.ts b/src/vs/platform/theme/common/theme.ts new file mode 100644 index 00000000000..90cef457b57 --- /dev/null +++ b/src/vs/platform/theme/common/theme.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Color scheme used by the OS and by color themes. + */ +export enum ColorScheme { + DARK = 'dark', + LIGHT = 'light', + HIGH_CONTRAST = 'hc' +} diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index e33a3e9cbd4..e936a4eb05a 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -10,6 +10,7 @@ import * as platform from 'vs/platform/registry/common/platform'; import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; import { Event, Emitter } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; export const IThemeService = createDecorator('themeService'); @@ -65,16 +66,10 @@ export namespace ThemeIcon { export const FileThemeIcon = { id: 'file' }; export const FolderThemeIcon = { id: 'folder' }; -// base themes -export const DARK: ThemeType = 'dark'; -export const LIGHT: ThemeType = 'light'; -export const HIGH_CONTRAST: ThemeType = 'hc'; -export type ThemeType = 'light' | 'dark' | 'hc'; - -export function getThemeTypeSelector(type: ThemeType): string { +export function getThemeTypeSelector(type: ColorScheme): string { switch (type) { - case DARK: return 'vs-dark'; - case HIGH_CONTRAST: return 'hc-black'; + case ColorScheme.DARK: return 'vs-dark'; + case ColorScheme.HIGH_CONTRAST: return 'hc-black'; default: return 'vs'; } } @@ -88,7 +83,7 @@ export interface ITokenStyle { export interface IColorTheme { - readonly type: ThemeType; + readonly type: ColorScheme; readonly label: string; diff --git a/src/vs/platform/theme/test/common/testThemeService.ts b/src/vs/platform/theme/test/common/testThemeService.ts index bc5fc2403f1..2bdd34d8eb0 100644 --- a/src/vs/platform/theme/test/common/testThemeService.ts +++ b/src/vs/platform/theme/test/common/testThemeService.ts @@ -4,14 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { IThemeService, IColorTheme, DARK, IFileIconTheme, ITokenStyle } from 'vs/platform/theme/common/themeService'; +import { IThemeService, IColorTheme, IFileIconTheme, ITokenStyle } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; export class TestColorTheme implements IColorTheme { public readonly label = 'test'; - constructor(private colors: { [id: string]: string; } = {}, public type = DARK) { + constructor(private colors: { [id: string]: string; } = {}, public type = ColorScheme.DARK) { } getColor(color: string, useDefault?: boolean): Color | undefined { diff --git a/src/vs/platform/userDataSync/common/keybindingsMerge.ts b/src/vs/platform/userDataSync/common/keybindingsMerge.ts index 3cbd0faf84d..de364130ad7 100644 --- a/src/vs/platform/userDataSync/common/keybindingsMerge.ts +++ b/src/vs/platform/userDataSync/common/keybindingsMerge.ts @@ -6,7 +6,7 @@ import * as objects from 'vs/base/common/objects'; import { parse } from 'vs/base/common/json'; import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; -import { firstIndex as findFirstIndex, equals } from 'vs/base/common/arrays'; +import { equals } from 'vs/base/common/arrays'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import * as contentUtil from 'vs/platform/userDataSync/common/content'; import { IStringDictionary } from 'vs/base/common/collections'; @@ -346,7 +346,7 @@ function removeKeybindings(content: string, command: string, formattingOptions: function updateKeybindings(content: string, command: string, keybindings: IUserFriendlyKeybinding[], formattingOptions: FormattingOptions): string { const allKeybindings = parseKeybindings(content); - const location = findFirstIndex(allKeybindings, keybinding => keybinding.command === command || keybinding.command === `-${command}`); + const location = allKeybindings.findIndex(keybinding => keybinding.command === command || keybinding.command === `-${command}`); // Remove all entries with this command for (let index = allKeybindings.length - 1; index >= 0; index--) { if (allKeybindings[index].command === command || allKeybindings[index].command === `-${command}`) { diff --git a/src/vs/platform/userDataSync/common/settingsMerge.ts b/src/vs/platform/userDataSync/common/settingsMerge.ts index f4333ac84aa..99a411448ae 100644 --- a/src/vs/platform/userDataSync/common/settingsMerge.ts +++ b/src/vs/platform/userDataSync/common/settingsMerge.ts @@ -10,7 +10,7 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions, Edit, getEOL } from 'vs/base/common/jsonFormatter'; import * as contentUtil from 'vs/platform/userDataSync/common/content'; import { IConflictSetting, getDisallowedIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync'; -import { firstIndex, distinct } from 'vs/base/common/arrays'; +import { distinct } from 'vs/base/common/arrays'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export interface IMergeResult { @@ -320,7 +320,7 @@ interface InsertLocation { function getInsertLocation(key: string, sourceTree: INode[], targetTree: INode[]): InsertLocation { - const sourceNodeIndex = firstIndex(sourceTree, (node => node.setting?.key === key)); + const sourceNodeIndex = sourceTree.findIndex(node => node.setting?.key === key); const sourcePreviousNode: INode = sourceTree[sourceNodeIndex - 1]; if (sourcePreviousNode) { diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index 0d9489ed8e9..a2cb34789eb 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -15,7 +15,6 @@ import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/service import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { assign } from 'vs/base/common/objects'; import { generateUuid } from 'vs/base/common/uuid'; import { isWeb } from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; @@ -355,10 +354,12 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync this.setDonotMakeRequestsUntil(undefined); const commonHeaders = await this.commonHeadersPromise; - options.headers = assign(options.headers || {}, commonHeaders, { + options.headers = { + ...(options.headers || {}), + ...commonHeaders, 'X-Account-Type': this.authToken.type, 'authorization': `Bearer ${this.authToken.token}`, - }); + }; // Add session headers this.addSessionHeaders(options.headers); diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index bdb6fbdf7f9..60690f56c38 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -11,6 +11,7 @@ import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platf import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { LogLevel } from 'vs/platform/log/common/log'; import { ExportData } from 'vs/base/common/performance'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; export interface IBaseOpenWindowsOptions { forceReuseWindow?: boolean; @@ -253,21 +254,3 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native export function zoomLevelToZoomFactor(zoomLevel = 0): number { return Math.pow(1.2, zoomLevel); } - -export enum ColorScheme { - - /** - * The window should use standard colors. - */ - DEFAULT = 1, - - /** - * The window should use dark colors. - */ - DARK = 2, - - /** - * The window should use high contrast colors. - */ - HIGH_CONTRAST = 3 -} diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 27381c0df22..10ecfbd55fa 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1358,7 +1358,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // For all other cases we first call into registerEmptyWindowBackupSync() to set it before // loading the window. if (options.emptyWindowBackupInfo) { - configuration.backupPath = join(this.environmentService.backupHome.fsPath, options.emptyWindowBackupInfo.backupFolder); + configuration.backupPath = join(this.environmentService.backupHome, options.emptyWindowBackupInfo.backupFolder); } let window: ICodeWindow | undefined; diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 780ddb90d21..680d2921f0f 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1160,7 +1160,7 @@ declare module 'vscode' { revealRange(range: Range, revealType?: TextEditorRevealType): void; /** - * ~~Show the text editor.~~ + * Show the text editor. * * @deprecated Use [window.showTextDocument](#window.showTextDocument) instead. * @@ -1170,7 +1170,7 @@ declare module 'vscode' { show(column?: ViewColumn): void; /** - * ~~Hide the text editor.~~ + * Hide the text editor. * * @deprecated Use the command `workbench.action.closeActiveEditor` instead. * This method shows unexpected behavior and will be removed in the next major update. @@ -2498,9 +2498,9 @@ declare module 'vscode' { } /** - * ~~MarkedString can be used to render human-readable text. It is either a markdown string + * MarkedString can be used to render human-readable text. It is either a markdown string * or a code-block that provides a language and a code snippet. Note that - * markdown strings will be sanitized - that means html will be escaped.~~ + * markdown strings will be sanitized - that means html will be escaped. * * @deprecated This type is deprecated, please use [`MarkdownString`](#MarkdownString) instead. */ @@ -2753,7 +2753,7 @@ declare module 'vscode' { constructor(name: string, kind: SymbolKind, containerName: string, location: Location); /** - * ~~Creates a new symbol information object.~~ + * Creates a new symbol information object. * * @deprecated Please use the constructor taking a [location](#Location) object. * @@ -3874,12 +3874,12 @@ declare module 'vscode' { /** * @deprecated Use `CompletionItem.insertText` and `CompletionItem.range` instead. * - * ~~An [edit](#TextEdit) which is applied to a document when selecting + * An [edit](#TextEdit) which is applied to a document when selecting * this completion. When an edit is provided the value of - * [insertText](#CompletionItem.insertText) is ignored.~~ + * [insertText](#CompletionItem.insertText) is ignored. * - * ~~The [range](#Range) of the edit must be single-line and on the same - * line completions were [requested](#CompletionItemProvider.provideCompletionItems) at.~~ + * The [range](#Range) of the edit must be single-line and on the same + * line completions were [requested](#CompletionItemProvider.provideCompletionItems) at. */ textEdit?: TextEdit; @@ -5220,7 +5220,7 @@ declare module 'vscode' { show(preserveFocus?: boolean): void; /** - * ~~Reveal this channel in the UI.~~ + * Reveal this channel in the UI. * * @deprecated Use the overload with just one parameter (`show(preserveFocus?: boolean): void`). * @@ -6179,7 +6179,7 @@ declare module 'vscode' { constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]); /** - * ~~Creates a new task.~~ + * Creates a new task. * * @deprecated Use the new constructors that allow specifying a scope for the task. * @@ -6266,7 +6266,7 @@ declare module 'vscode' { * @param token A cancellation token. * @return an array of tasks */ - provideTasks(token?: CancellationToken): ProviderResult; + provideTasks(token: CancellationToken): ProviderResult; /** * Resolves a task that has no [`execution`](#Task.execution) set. Tasks are @@ -6281,7 +6281,7 @@ declare module 'vscode' { * @param token A cancellation token. * @return The resolved task */ - resolveTask(task: T, token?: CancellationToken): ProviderResult; + resolveTask(task: T, token: CancellationToken): ProviderResult; } /** @@ -8134,8 +8134,8 @@ declare module 'vscode' { export function setStatusBarMessage(text: string): Disposable; /** - * ~~Show progress in the Source Control viewlet while running the given callback and while - * its returned promise isn't resolve or rejected.~~ + * Show progress in the Source Control viewlet while running the given callback and while + * its returned promise isn't resolve or rejected. * * @deprecated Use `withProgress` instead. * @@ -9571,8 +9571,8 @@ declare module 'vscode' { export const fs: FileSystem; /** - * ~~The folder that is open in the editor. `undefined` when no folder - * has been opened.~~ + * The folder that is open in the editor. `undefined` when no folder + * has been opened. * * @deprecated Use [`workspaceFolders`](#workspace.workspaceFolders) instead. */ @@ -9952,7 +9952,7 @@ declare module 'vscode' { export const onDidChangeConfiguration: Event; /** - * ~~Register a task provider.~~ + * Register a task provider. * * @deprecated Use the corresponding function on the `tasks` namespace instead * @@ -10691,8 +10691,8 @@ declare module 'vscode' { export namespace scm { /** - * ~~The [input box](#SourceControlInputBox) for the last source control - * created by the extension.~~ + * The [input box](#SourceControlInputBox) for the last source control + * created by the extension. * * @deprecated Use SourceControl.inputBox instead */ diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 4325d62d7be..b732e88f1ec 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -972,6 +972,11 @@ declare module 'vscode' { */ tooltip?: string | MarkdownString | /* for compilation */ any; + /** + * When `iconPath` is a [ThemeColor](#ThemeColor) `iconColor` will be used to set the color of the icon. + */ + iconColor?: ThemeColor; + /** * @param label Label describing this item * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) @@ -1322,7 +1327,6 @@ declare module 'vscode' { readonly isUntitled: boolean; readonly cells: ReadonlyArray; languages: string[]; - displayOrder?: GlobPattern[]; metadata: NotebookDocumentMetadata; } @@ -2058,6 +2062,11 @@ declare module 'vscode' { */ title?: string; + /** + * Human-readable string which is rendered less prominently in the title. + */ + description?: string; + /** * Event fired when the view is disposed. * @@ -2086,6 +2095,15 @@ declare module 'vscode' { * Note that hiding a view using the context menu instead disposes of the view and fires `onDidDispose`. */ readonly onDidChangeVisibility: Event; + + /** + * Reveal the view in the UI. + * + * If the view is collapsed, this will expand it. + * + * @param preserveFocus When `true` the view will not take focus. + */ + show(preserveFocus?: boolean): void; } interface WebviewViewResolveContext { diff --git a/src/vs/workbench/api/browser/mainThreadFileSystem.ts b/src/vs/workbench/api/browser/mainThreadFileSystem.ts index c68894ca077..9c6e733d8ff 100644 --- a/src/vs/workbench/api/browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/browser/mainThreadFileSystem.ts @@ -34,7 +34,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { } $unregisterProvider(handle: number): void { - dispose(this._fileProvider.get(handle)); + this._fileProvider.get(handle)?.dispose(); this._fileProvider.delete(handle); } diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index b0c3cc0dadd..ef92249a326 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -7,15 +7,17 @@ import * as DOM from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { combinedDisposable, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { ResourceMap } from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, INotebookDocumentFilter, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDocumentFilter, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookCellsChangeType, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; @@ -58,7 +60,7 @@ class DocumentAndEditorState { const apiEditors = []; for (let id in after.textEditors) { const editor = after.textEditors.get(id)!; - apiEditors.push({ id, documentUri: editor.uri!, selections: editor!.textModel!.selections, visibleRanges: editor.visibleRanges }); + apiEditors.push({ id, documentUri: editor.uri!, selections: editor!.getSelectionHandles(), visibleRanges: editor.visibleRanges }); } return { @@ -72,7 +74,7 @@ class DocumentAndEditorState { const addedAPIEditors = editorDelta.added.map(add => ({ id: add.getId(), documentUri: add.uri!, - selections: add.textModel!.selections || [], + selections: add.getSelectionHandles(), visibleRanges: add.visibleRanges })); @@ -128,13 +130,13 @@ class DocumentAndEditorState { @extHostNamedCustomer(MainContext.MainThreadNotebook) export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape { - private readonly _notebookProviders = new Map(); + private readonly _notebookProviders = new Map(); private readonly _notebookKernelProviders = new Map, provider: IDisposable }>(); private readonly _proxy: ExtHostNotebookShape; private _toDisposeOnEditorRemove = new Map(); private _currentState?: DocumentAndEditorState; private _editorEventListenersMapping: Map = new Map(); - private _documentEventListenersMapping: Map = new Map(); + private _documentEventListenersMapping: ResourceMap = new ResourceMap(); private readonly _cellStatusBarEntries: Map = new Map(); constructor( @@ -162,11 +164,11 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } async removeNotebookTextModel(uri: URI): Promise { + // TODO@rebornix remove this? obsolete? // TODO@rebornix, remove cell should use emitDelta as well to ensure document/editor events are sent together this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [uri] }); - let textModelDisposableStore = this._documentEventListenersMapping.get(uri.toString()); - textModelDisposableStore?.dispose(); - this._documentEventListenersMapping.delete(URI.from(uri).toString()); + this._documentEventListenersMapping.get(uri)?.dispose(); + this._documentEventListenersMapping.delete(uri); } private _isDeltaEmpty(delta: INotebookDocumentsAndEditorsDelta) { @@ -230,7 +232,12 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo if (!this._editorEventListenersMapping.has(editor.getId())) { const disposableStore = new DisposableStore(); disposableStore.add(editor.onDidChangeVisibleRanges(() => { - this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: { ranges: editor.visibleRanges } }); + this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: { ranges: editor.visibleRanges }, selections: null }); + })); + + disposableStore.add(editor.onDidChangeSelection(() => { + const selectionHandles = editor.getSelectionHandles(); + this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: null, selections: { selections: selectionHandles } }); })); this._editorEventListenersMapping.set(editor.getId(), disposableStore); @@ -255,40 +262,60 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo notebookEditorAddedHandler(editor); }); - const notebookDocumentAddedHandler = (doc: URI) => { - if (!this._editorEventListenersMapping.has(doc.toString())) { + const cellToDto = (cell: NotebookCellTextModel): IMainCellDto => { + return { + handle: cell.handle, + uri: cell.uri, + source: cell.textBuffer.getLinesContent(), + eol: cell.textBuffer.getEOL(), + language: cell.language, + cellKind: cell.cellKind, + outputs: cell.outputs, + metadata: cell.metadata + }; + }; + + + const notebookDocumentAddedHandler = (textModel: NotebookTextModel) => { + if (!this._editorEventListenersMapping.has(textModel.uri.toString())) { const disposableStore = new DisposableStore(); - const textModel = this._notebookService.getNotebookTextModel(doc); - disposableStore.add(textModel!.onDidModelChangeProxy(e => { - this._proxy.$acceptModelChanged(textModel!.uri, e, textModel!.isDirty); - this._proxy.$acceptDocumentPropertiesChanged(doc, { selections: { selections: textModel!.selections }, metadata: null }); - })); - disposableStore.add(textModel!.onDidSelectionChange(e => { - const selectionsChange = e ? { selections: e } : null; - this._proxy.$acceptDocumentPropertiesChanged(doc, { selections: selectionsChange, metadata: null }); + disposableStore.add(textModel!.onDidChangeContent(e => { + const data = + e.kind === NotebookCellsChangeType.ModelChange || e.kind === NotebookCellsChangeType.Initialize + ? { + kind: e.kind, + versionId: e.versionId, + changes: e.changes.map(diff => [diff[0], diff[1], diff[2].map(cell => cellToDto(cell as NotebookCellTextModel))] as [number, number, IMainCellDto[]]) + } + : ( + e.kind === NotebookCellsChangeType.Move + ? { + kind: e.kind, + index: e.index, + length: e.length, + newIdx: e.newIdx, + versionId: e.versionId, + cells: e.cells.map(cell => cellToDto(cell as NotebookCellTextModel)) + } + : e + ); + this._proxy.$acceptModelChanged(textModel.uri, data, textModel.isDirty); + this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: null }); })); this._editorEventListenersMapping.set(textModel!.uri.toString(), disposableStore); } }; - this._register(this._notebookService.onNotebookDocumentAdd((documents) => { - documents.forEach(doc => { - notebookDocumentAddedHandler(doc); - }); + this._notebookService.listNotebookDocuments().forEach(notebookDocumentAddedHandler); + this._register(this._notebookService.onDidAddNotebookDocument(document => { + notebookDocumentAddedHandler(document); this._updateState(); })); - this._notebookService.listNotebookDocuments().forEach((doc) => { - notebookDocumentAddedHandler(doc.uri); - }); - - this._register(this._notebookService.onNotebookDocumentRemove((documents) => { - documents.forEach(doc => { - this._documentEventListenersMapping.get(doc.toString())?.dispose(); - this._documentEventListenersMapping.delete(doc.toString()); - }); - + this._register(this._notebookService.onDidRemoveNotebookDocument(uri => { + this._documentEventListenersMapping.get(uri)?.dispose(); + this._documentEventListenersMapping.delete(uri); this._updateState(); })); @@ -403,16 +430,12 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo // } } - async $registerNotebookProvider(_extension: NotebookExtensionDescription, _viewType: string, _supportBackup: boolean, options: { transientOutputs: boolean; transientMetadata: TransientMetadata }): Promise { + async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, options: { transientOutputs: boolean; transientMetadata: TransientMetadata }): Promise { const controller: IMainNotebookController = { - supportBackup: _supportBackup, - options: options, + supportBackup, + options, reloadNotebook: async (mainthreadTextModel: NotebookTextModel) => { - const data = await this._proxy.$resolveNotebookData(_viewType, mainthreadTextModel.uri); - if (!data) { - return; - } - + const data = await this._proxy.$resolveNotebookData(viewType, mainthreadTextModel.uri); mainthreadTextModel.updateLanguages(data.languages); mainthreadTextModel.metadata = data.metadata; mainthreadTextModel.transientOptions = options; @@ -432,10 +455,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo createNotebook: async (textModel: NotebookTextModel, backupId?: string) => { // open notebook document const data = await this._proxy.$resolveNotebookData(textModel.viewType, textModel.uri, backupId); - if (!data) { - return; - } - textModel.updateLanguages(data.languages); textModel.metadata = data.metadata; textModel.transientOptions = options; @@ -447,7 +466,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo textModel.insertTemplateCell(mainCell); } - this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { selections: null, metadata: textModel.metadata }); + this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: textModel.metadata }); return; }, resolveNotebookEditor: async (viewType: string, uri: URI, editorId: string) => { @@ -460,30 +479,32 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo return this.removeNotebookTextModel(uri); }, save: async (uri: URI, token: CancellationToken) => { - return this._proxy.$saveNotebook(_viewType, uri, token); + return this._proxy.$saveNotebook(viewType, uri, token); }, saveAs: async (uri: URI, target: URI, token: CancellationToken) => { - return this._proxy.$saveNotebookAs(_viewType, uri, target, token); + return this._proxy.$saveNotebookAs(viewType, uri, target, token); }, backup: async (uri: URI, token: CancellationToken) => { - return this._proxy.$backup(_viewType, uri, token); + return this._proxy.$backup(viewType, uri, token); } }; - this._notebookProviders.set(_viewType, controller); - this._notebookService.registerNotebookController(_viewType, _extension, controller); + const disposable = this._notebookService.registerNotebookController(viewType, extension, controller); + this._notebookProviders.set(viewType, { controller, disposable }); return; } - async $onNotebookChange(viewType: string, uri: UriComponents): Promise { + async $onNotebookChange(_viewType: string, uri: UriComponents): Promise { const textModel = this._notebookService.getNotebookTextModel(URI.from(uri)); textModel?.handleUnknownChange(); } async $unregisterNotebookProvider(viewType: string): Promise { - this._notebookProviders.delete(viewType); - this._notebookService.unregisterNotebookProvider(viewType); - return; + const entry = this._notebookProviders.get(viewType); + if (entry) { + entry.disposable.dispose(); + this._notebookProviders.delete(viewType); + } } async $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise { @@ -555,7 +576,16 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo async $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata): Promise { this.logService.debug('MainThreadNotebooks#updateNotebookCellMetadata', resource.path, handle, metadata); const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); - textModel?.changeCellMetadata(handle, metadata, true); + if (!textModel) { + return; + } + + const index = textModel.cells.findIndex(cell => cell.handle === handle); + if (index < 0) { + return; + } + + textModel.applyEdit(textModel.versionId, [{ editType: CellEditType.Metadata, index, metadata }], true); } async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[]): Promise { @@ -564,7 +594,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo if (textModel) { this._notebookService.transformSpliceOutputs(textModel, splices); - textModel.spliceNotebookCellOutputs(cellHandle, splices); + textModel._spliceNotebookCellOutputs(cellHandle, splices); } } diff --git a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts index 248af0860e6..eae46c714d0 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts @@ -29,13 +29,20 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc } public $setWebviewViewTitle(handle: extHostProtocol.WebviewHandle, value: string | undefined): void { - const webviewView = this._webviewViews.get(handle); - if (!webviewView) { - throw new Error('unknown webview view'); - } + const webviewView = this.getWebviewView(handle); webviewView.title = value; } + public $setWebviewViewDescription(handle: extHostProtocol.WebviewHandle, value: string | undefined): void { + const webviewView = this.getWebviewView(handle); + webviewView.description = value; + } + + public $show(handle: extHostProtocol.WebviewHandle, preserveFocus: boolean): void { + const webviewView = this.getWebviewView(handle); + webviewView.show(preserveFocus); + } + public $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void { if (this._webviewViewProviders.has(viewType)) { throw new Error(`View provider for ${viewType} already registered`); @@ -89,5 +96,13 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc provider.dispose(); this._webviewViewProviders.delete(viewType); } + + private getWebviewView(handle: string): WebviewView { + const webviewView = this._webviewViews.get(handle); + if (!webviewView) { + throw new Error('unknown webview view'); + } + return webviewView; + } } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 94a655ceb78..da717e7f0e1 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -51,7 +51,7 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; -import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { Dto } from 'vs/base/common/types'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; @@ -640,6 +640,9 @@ export interface MainThreadWebviewViewsShape extends IDisposable { $unregisterWebviewViewProvider(viewType: string): void; $setWebviewViewTitle(handle: WebviewHandle, value: string | undefined): void; + $setWebviewViewDescription(handle: WebviewHandle, value: string | undefined): void; + + $show(handle: WebviewHandle, preserveFocus: boolean): void; } export interface WebviewPanelViewStateData { @@ -1639,11 +1642,11 @@ export interface INotebookVisibleRangesEvent { export interface INotebookEditorPropertiesChangeData { visibleRanges: INotebookVisibleRangesEvent | null; + selections: INotebookSelectionChangeEvent | null; } export interface INotebookDocumentPropertiesChangeData { metadata: NotebookDocumentMetadata | null; - selections: INotebookSelectionChangeEvent | null; } export interface INotebookModelAddedData { @@ -1672,7 +1675,7 @@ export interface INotebookDocumentsAndEditorsDelta { } export interface ExtHostNotebookShape { - $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise; + $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise; $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise; $provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise; $resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise; @@ -1685,7 +1688,7 @@ export interface ExtHostNotebookShape { $acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void; $acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelId: string | undefined }): void; $onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void; - $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent, isDirty: boolean): void; + $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void; $acceptModelSaved(uriComponents: UriComponents): void; $acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void; $acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void; diff --git a/src/vs/workbench/api/common/extHostFileSystem.ts b/src/vs/workbench/api/common/extHostFileSystem.ts index 9c0ee143a8b..34d7d3862ed 100644 --- a/src/vs/workbench/api/common/extHostFileSystem.ts +++ b/src/vs/workbench/api/common/extHostFileSystem.ts @@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystemShape, IFileChangeDto } from './extHost.protocol'; 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 { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { FileChangeType } from 'vs/workbench/api/common/extHostTypes'; import * as typeConverter from 'vs/workbench/api/common/extHostTypeConverters'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures'; @@ -127,7 +127,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { } dispose(): void { - dispose(this._linkProviderRegistration); + this._linkProviderRegistration?.dispose(); } private _registerLinkProviderIfNotYetRegistered(): void { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index c912e48141d..80258e2b7d7 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -25,7 +25,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IURITransformer } from 'vs/base/common/uriIpc'; -import { DisposableStore, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; import { encodeSemanticTokensDto } from 'vs/workbench/api/common/shared/semanticTokensDto'; import { IdGenerator } from 'vs/base/common/idGenerator'; @@ -177,7 +177,7 @@ class CodeLensAdapter { } releaseCodeLenses(cachedId: number): void { - dispose(this._disposables.get(cachedId)); + this._disposables.get(cachedId)?.dispose(); this._disposables.delete(cachedId); this._cache.delete(cachedId); } @@ -455,7 +455,7 @@ class CodeActionAdapter { } public releaseCodeActions(cachedId: number): void { - dispose(this._disposables.get(cachedId)); + this._disposables.get(cachedId)?.dispose(); this._disposables.delete(cachedId); this._cache.delete(cachedId); } @@ -938,7 +938,7 @@ class SuggestAdapter { } releaseCompletionItems(id: number): any { - dispose(this._disposables.get(id)); + this._disposables.get(id)?.dispose(); this._disposables.delete(id); this._cache.delete(id); } diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 0566d3c40b5..03e7cedad80 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -22,7 +22,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; -import { addIdToOutput, CellEditType, CellOutputKind, CellStatusbarAlignment, CellUri, diff, ICellEditOperation, ICellReplaceEdit, IMainCellDto, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEvent, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { addIdToOutput, CellEditType, CellOutputKind, CellStatusbarAlignment, CellUri, diff, ICellEditOperation, ICellReplaceEdit, IMainCellDto, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; import { Cache } from './cache'; import { ResourceMap } from 'vs/base/common/map'; @@ -212,10 +212,8 @@ export class ExtHostNotebookDocument extends Disposable { private _cellDisposableMapping = new Map(); private _notebook: vscode.NotebookDocument | undefined; - - private _metadata: Required = notebookDocumentMetadataDefaults; + private _metadata: Required; private _metadataChangeListener: IDisposable; - private _displayOrder: string[] = []; private _versionId = 0; private _isDirty: boolean = false; private _backupCounter = 1; @@ -230,13 +228,14 @@ export class ExtHostNotebookDocument extends Disposable { private readonly _documentsAndEditors: ExtHostDocumentsAndEditors, private readonly _emitter: INotebookEventEmitter, private readonly _viewType: string, + metadata: Required, public readonly uri: URI, public readonly renderingHandler: ExtHostNotebookOutputRenderingHandler, private readonly _storagePath: URI | undefined ) { super(); - const observableMetadata = getObservable(notebookDocumentMetadataDefaults); + const observableMetadata = getObservable(metadata); this._metadata = observableMetadata.proxy; this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { this._tryUpdateMetadata(); @@ -284,8 +283,6 @@ export class ExtHostNotebookDocument extends Disposable { get cells(): ReadonlyArray { return that._cells.map(cell => cell.cell); }, get languages() { return that._languages; }, set languages(value: string[]) { that._trySetLanguages(value); }, - get displayOrder() { return that._displayOrder; }, - set displayOrder(value: string[]) { that._displayOrder = value; }, get metadata() { return that._metadata; }, set metadata(value: Required) { that._updateMetadata(value); }, }); @@ -316,7 +313,7 @@ export class ExtHostNotebookDocument extends Disposable { this._backup = undefined; } - acceptModelChanged(event: NotebookCellsChangedEvent, isDirty: boolean): void { + acceptModelChanged(event: NotebookCellsChangedEventDto, isDirty: boolean): void { this._versionId = event.versionId; this._isDirty = isDirty; if (event.kind === NotebookCellsChangeType.Initialize) { @@ -327,13 +324,9 @@ export class ExtHostNotebookDocument extends Disposable { this._moveCell(event.index, event.newIdx); } else if (event.kind === NotebookCellsChangeType.Output) { this._setCellOutputs(event.index, event.outputs); - } else if (event.kind === NotebookCellsChangeType.CellClearOutput) { - this._clearCellOutputs(event.index); - } else if (event.kind === NotebookCellsChangeType.CellsClearOutput) { - this._clearAllCellOutputs(); } else if (event.kind === NotebookCellsChangeType.ChangeLanguage) { this._changeCellLanguage(event.index, event.language); - } else if (event.kind === NotebookCellsChangeType.ChangeMetadata) { + } else if (event.kind === NotebookCellsChangeType.ChangeCellMetadata) { this._changeCellMetadata(event.index, event.metadata); } } @@ -424,25 +417,6 @@ export class ExtHostNotebookDocument extends Disposable { this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] }); } - private _clearCellOutputs(index: number): void { - const cell = this._cells[index].cell; - cell.outputs = []; - const event: vscode.NotebookCellOutputsChangeEvent = { document: this.notebookDocument, cells: [cell] }; - this._emitter.emitCellOutputsChange(event); - } - - private _clearAllCellOutputs(): void { - const modifedCells: vscode.NotebookCell[] = []; - this._cells.forEach(({ cell }) => { - if (cell.outputs.length !== 0) { - cell.outputs = []; - modifedCells.push(cell); - } - }); - const event: vscode.NotebookCellOutputsChangeEvent = { document: this.notebookDocument, cells: modifedCells }; - this._emitter.emitCellOutputsChange(event); - } - private _changeCellLanguage(index: number, language: string): void { const cell = this._cells[index]; const event: vscode.NotebookCellLanguageChangeEvent = { document: this.notebookDocument, cell: cell.cell, language }; @@ -904,7 +878,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private readonly _notebookKernels = new Map(); private readonly _notebookKernelProviders = new Map(); private readonly _documents = new ResourceMap(); - private readonly _unInitializedDocuments = new ResourceMap(); private readonly _editors = new Map(); private readonly _webviewComm = new Map(); private readonly _commandsConverter: CommandsConverter; @@ -1076,49 +1049,24 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN }); } - async $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise { + async $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise { const provider = this._notebookContentProviders.get(viewType); - const revivedUri = URI.revive(uri); if (!provider) { - return; + throw new Error(`NO provider for '${viewType}'`); } - const storageRoot = this._extensionStoragePaths.workspaceValue(provider.extension) ?? this._extensionStoragePaths.globalValue(provider.extension); - let document = this._documents.get(revivedUri); - - if (!document) { - const that = this; - document = this._unInitializedDocuments.get(revivedUri) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, { - emitModelChange(event: vscode.NotebookCellsChangeEvent): void { - that._onDidChangeNotebookCells.fire(event); - }, - emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void { - that._onDidChangeCellOutputs.fire(event); - }, - emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void { - that._onDidChangeCellLanguage.fire(event); - }, - emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { - that._onDidChangeCellMetadata.fire(event); - }, - }, viewType, revivedUri, this, storageRoot); - this._unInitializedDocuments.set(revivedUri, document); - } - - const rawCells = await provider.provider.openNotebook(URI.revive(uri), { backupId }); - const dto = { + const data = await provider.provider.openNotebook(URI.revive(uri), { backupId }); + return { metadata: { ...notebookDocumentMetadataDefaults, - ...rawCells.metadata + ...data.metadata }, - languages: rawCells.languages, - cells: rawCells.cells.map(cell => ({ + languages: data.languages, + cells: data.cells.map(cell => ({ ...cell, outputs: cell.outputs.map(o => addIdToOutput(o)) })), }; - - return dto; } async $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise { @@ -1259,7 +1207,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } } - // TODO: remove document - editor one on one mapping + // TODO@rebornix: remove document - editor one on one mapping private _getEditorFromURI(uriComponents: UriComponents) { const uriStr = URI.revive(uriComponents).toString(); let editor: { editor: ExtHostNotebookEditor; } | undefined; @@ -1276,7 +1224,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN this._webviewComm.get(editorId)?.onDidReceiveMessage(forRendererType, message); } - $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent, isDirty: boolean): void { + $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void { const document = this._documents.get(URI.revive(uriComponents)); if (document) { document.acceptModelChanged(event, isDirty); @@ -1312,15 +1260,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN visibleRanges: editor.editor.visibleRanges }); } - } - - $acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void { - this.logService.debug('ExtHostNotebook#$acceptDocumentPropertiesChanged', uriComponents.path, data); - const editor = this._getEditorFromURI(uriComponents); - - if (!editor) { - return; - } if (data.selections) { if (data.selections.selections.length) { @@ -1335,7 +1274,15 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN selection: editor.editor.selection }); } + } + $acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void { + this.logService.debug('ExtHostNotebook#$acceptDocumentPropertiesChanged', uriComponents.path, data); + const editor = this._getEditorFromURI(uriComponents); + + if (!editor) { + return; + } if (data.metadata) { editor.editor.notebookData.notebookDocument.metadata = { @@ -1406,64 +1353,55 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const addedCellDocuments: IModelAddedData[] = []; for (const modelData of delta.addedDocuments) { - const revivedUri = URI.revive(modelData.uri); + const uri = URI.revive(modelData.uri); const viewType = modelData.viewType; const entry = this._notebookContentProviders.get(viewType); const storageRoot = entry && (this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension)); + if (this._documents.has(uri)) { + throw new Error(`adding EXISTING notebook ${uri}`); + } + const that = this; - if (!this._documents.has(revivedUri)) { - const that = this; - - const document = this._unInitializedDocuments.get(revivedUri) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, { - emitModelChange(event: vscode.NotebookCellsChangeEvent): void { - that._onDidChangeNotebookCells.fire(event); - }, - emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void { - that._onDidChangeCellOutputs.fire(event); - }, - emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void { - that._onDidChangeCellLanguage.fire(event); - }, - emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { - that._onDidChangeCellMetadata.fire(event); - } - }, viewType, revivedUri, this, storageRoot); - - this._unInitializedDocuments.delete(revivedUri); - if (modelData.metadata) { - document.notebookDocument.metadata = { - ...notebookDocumentMetadataDefaults, - ...modelData.metadata - }; + const document = new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, { + emitModelChange(event: vscode.NotebookCellsChangeEvent): void { + that._onDidChangeNotebookCells.fire(event); + }, + emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void { + that._onDidChangeCellOutputs.fire(event); + }, + emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void { + that._onDidChangeCellLanguage.fire(event); + }, + emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { + that._onDidChangeCellMetadata.fire(event); } + }, viewType, { ...notebookDocumentMetadataDefaults, ...modelData.metadata }, uri, this, storageRoot); - document.acceptModelChanged({ - kind: NotebookCellsChangeType.Initialize, - versionId: modelData.versionId, - changes: [[ - 0, - 0, - modelData.cells - ]] - }, false); + document.acceptModelChanged({ + kind: NotebookCellsChangeType.Initialize, + versionId: modelData.versionId, + changes: [[ + 0, + 0, + modelData.cells + ]] + }, false); - // add cell document as vscode.TextDocument - addedCellDocuments.push(...modelData.cells.map(cell => ExtHostCell.asModelAddData(document.notebookDocument, cell))); + // add cell document as vscode.TextDocument + addedCellDocuments.push(...modelData.cells.map(cell => ExtHostCell.asModelAddData(document.notebookDocument, cell))); - this._documents.get(revivedUri)?.dispose(); - this._documents.set(revivedUri, document); + this._documents.get(uri)?.dispose(); + this._documents.set(uri, document); - // create editor if populated - if (modelData.attachedEditor) { - this._createExtHostEditor(document, modelData.attachedEditor.id, modelData.attachedEditor.selections, modelData.attachedEditor.visibleRanges); - editorChanged = true; - } + // create editor if populated + if (modelData.attachedEditor) { + this._createExtHostEditor(document, modelData.attachedEditor.id, modelData.attachedEditor.selections, modelData.attachedEditor.visibleRanges); + editorChanged = true; } this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments }); - const document = this._documents.get(revivedUri)!; this._onDidOpenNotebookDocument.fire(document.notebookDocument); } } diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 8621743e706..02777446909 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -13,7 +13,7 @@ import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.proto import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions } from 'vs/workbench/common/views'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { asPromise } from 'vs/base/common/async'; -import { TreeItemCollapsibleState, ThemeIcon, MarkdownString as MarkdownStringType } from 'vs/workbench/api/common/extHostTypes'; +import { TreeItemCollapsibleState, ThemeIcon, MarkdownString as MarkdownStringType, ThemeColor } from 'vs/workbench/api/common/extHostTypes'; import { isUndefinedOrNull, isString } from 'vs/base/common/types'; import { equals, coalesce } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; @@ -550,6 +550,7 @@ class ExtHostTreeView extends Disposable { icon, iconDark: this.getDarkIconPath(extensionTreeItem) || icon, themeIcon: extensionTreeItem.iconPath instanceof ThemeIcon ? { id: extensionTreeItem.iconPath.id } : undefined, + iconColor: this.getIconColor(extensionTreeItem), collapsibleState: isUndefinedOrNull(extensionTreeItem.collapsibleState) ? TreeItemCollapsibleState.None : extensionTreeItem.collapsibleState, accessibilityInformation: extensionTreeItem.accessibilityInformation }; @@ -563,6 +564,11 @@ class ExtHostTreeView extends Disposable { }; } + private getIconColor(extensionTreeItem: vscode.TreeItem2): ThemeColor | undefined { + checkProposedApiEnabled(this.extension); + return (extensionTreeItem.iconPath instanceof ThemeIcon) ? extensionTreeItem.iconColor : undefined; + } + private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent: TreeNode | Root, returnFirst?: boolean): TreeItemHandle { if (id) { return `${ExtHostTreeView.ID_HANDLE_PREFIX}/${id}`; diff --git a/src/vs/workbench/api/common/extHostWebviewView.ts b/src/vs/workbench/api/common/extHostWebviewView.ts index cc23d2a1fcd..dcb76f3afd3 100644 --- a/src/vs/workbench/api/common/extHostWebviewView.ts +++ b/src/vs/workbench/api/common/extHostWebviewView.ts @@ -23,6 +23,7 @@ class ExtHostWebviewView extends Disposable implements vscode.WebviewView { #isDisposed = false; #isVisible: boolean; #title: string | undefined; + #description: string | undefined; constructor( handle: extHostProtocol.WebviewHandle, @@ -70,6 +71,19 @@ class ExtHostWebviewView extends Disposable implements vscode.WebviewView { } } + public get description(): string | undefined { + this.assertNotDisposed(); + return this.#description; + } + + public set description(value: string | undefined) { + this.assertNotDisposed(); + if (this.#description !== value) { + this.#description = value; + this.#proxy.$setWebviewViewDescription(this.#handle, value); + } + } + public get visible(): boolean { return this.#isVisible; } public get webview(): vscode.Webview { return this.#webview; } @@ -85,6 +99,11 @@ class ExtHostWebviewView extends Disposable implements vscode.WebviewView { this.#onDidChangeVisibility.fire(); } + public show(preserveFocus?: boolean): void { + this.assertNotDisposed(); + this.#proxy.$show(this.#handle, !!preserveFocus); + } + private assertNotDisposed() { if (this.#isDisposed) { throw new Error('Webview is disposed'); diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 1de01f036f3..cd77e992cdc 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; -import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorIsReadonlyContext, EditorAreaVisibleContext, DirtyWorkingCopiesContext, ActiveEditorAvailableEditorIdsContext } from 'vs/workbench/common/editor'; +import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, EditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext } from 'vs/workbench/common/editor'; import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -21,14 +21,12 @@ import { PanelPositionContext } from 'vs/workbench/common/panel'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -export const RemoteNameContext = new RawContextKey('remoteName', ''); -export const RemoteConnectionState = new RawContextKey<'' | 'initializing' | 'disconnected' | 'connected'>('remoteConnectionState', ''); - export const WorkbenchStateContext = new RawContextKey('workbenchState', undefined); - export const WorkspaceFolderCountContext = new RawContextKey('workspaceFolderCount', 0); -export const RemoteFileDialogContext = new RawContextKey('remoteFileDialogVisible', false); +export const DirtyWorkingCopiesContext = new RawContextKey('dirtyWorkingCopies', false); + +export const RemoteNameContext = new RawContextKey('remoteName', ''); export const IsFullscreenContext = new RawContextKey('isFullscreen', false); @@ -89,7 +87,7 @@ export class WorkbenchContextKeysHandler extends Disposable { // Editors this.activeEditorContext = ActiveEditorContext.bindTo(this.contextKeyService); - this.activeEditorIsReadonly = ActiveEditorIsReadonlyContext.bindTo(this.contextKeyService); + this.activeEditorIsReadonly = EditorReadonlyContext.bindTo(this.contextKeyService); this.activeEditorAvailableEditorIds = ActiveEditorAvailableEditorIdsContext.bindTo(this.contextKeyService); this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService); this.textCompareEditorVisibleContext = TextCompareEditorVisibleContext.bindTo(this.contextKeyService); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 0045a91e2a6..14469f93bed 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -18,7 +18,7 @@ import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeServic import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BORDER } from 'vs/workbench/common/theme'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar'; -import { Dimension, addClass, removeNode, createCSSRule, asCSSUrl, toggleClass, addDisposableListener, EventType } from 'vs/base/browser/dom'; +import { Dimension, createCSSRule, asCSSUrl, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -404,7 +404,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { } if (this.menuBarContainer) { - removeNode(this.menuBarContainer); + this.menuBarContainer.remove(); this.menuBarContainer = undefined; this.registerKeyboardNavigationListeners(); } @@ -412,7 +412,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { private installMenubar() { this.menuBarContainer = document.createElement('div'); - addClass(this.menuBarContainer, 'menubar'); + this.menuBarContainer.classList.add('menubar'); const content = assertIsDefined(this.content); content.prepend(this.menuBarContainer); @@ -428,7 +428,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.element = parent; this.content = document.createElement('div'); - addClass(this.content, 'content'); + this.content.classList.add('content'); parent.appendChild(this.content); // Home action bar @@ -454,7 +454,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { // Global action bar this.globalActivitiesContainer = document.createElement('div'); - addClass(this.globalActivitiesContainer, 'global-activity'); + this.globalActivitiesContainer.classList.add('global-activity'); this.content.appendChild(this.globalActivitiesContainer); this.createGlobalActivityActionBar(this.globalActivitiesContainer); @@ -535,7 +535,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.homeBarContainer = document.createElement('div'); this.homeBarContainer.setAttribute('aria-label', nls.localize('homeIndicator', "Home")); this.homeBarContainer.setAttribute('role', 'toolbar'); - addClass(this.homeBarContainer, 'home-bar'); + this.homeBarContainer.classList.add('home-bar'); this.homeBar = this._register(new ActionBar(this.homeBarContainer, { orientation: ActionsOrientation.VERTICAL, @@ -547,7 +547,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { })); const homeBarIconBadge = document.createElement('div'); - addClass(homeBarIconBadge, 'home-bar-icon-badge'); + homeBarIconBadge.classList.add('home-bar-icon-badge'); this.homeBarContainer.appendChild(homeBarIconBadge); this.homeBar.push(this._register(this.instantiationService.createInstance(HomeAction, href, title, icon))); @@ -563,7 +563,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { container.style.backgroundColor = background; const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || ''; - toggleClass(container, 'bordered', !!borderColor); + container.classList.toggle('bordered', !!borderColor); container.style.borderColor = borderColor ? borderColor : ''; } diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index f029a2c190c..4a674fc637a 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -12,7 +12,7 @@ import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { CompositeActionViewItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionViewItem, ActivityAction, ICompositeBar, ICompositeBarColors } from 'vs/workbench/browser/parts/compositeBarActions'; -import { Dimension, $, addDisposableListener, EventType, EventHelper, toggleClass, isAncestor } from 'vs/base/browser/dom'; +import { Dimension, $, addDisposableListener, EventType, EventHelper, isAncestor } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { Widget } from 'vs/base/browser/ui/widget'; @@ -285,8 +285,8 @@ export class CompositeBar extends Widget implements ICompositeBar { } private updateFromDragging(element: HTMLElement, showFeedback: boolean, front: boolean): Before2D | undefined { - toggleClass(element, 'dragged-over-head', showFeedback && front); - toggleClass(element, 'dragged-over-tail', showFeedback && !front); + element.classList.toggle('dragged-over-head', showFeedback && front); + element.classList.toggle('dragged-over-tail', showFeedback && !front); if (!showFeedback) { return undefined; diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index 9f358b6e25d..ac10772a1d9 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -205,7 +205,7 @@ export class ActivityActionViewItem extends BaseActionViewItem { // Try hard to prevent keyboard only focus feedback when using mouse this._register(dom.addDisposableListener(this.container, dom.EventType.MOUSE_DOWN, () => { - dom.addClass(this.container, 'clicked'); + this.container.classList.add('clicked'); })); this._register(dom.addDisposableListener(this.container, dom.EventType.MOUSE_UP, () => { @@ -214,7 +214,7 @@ export class ActivityActionViewItem extends BaseActionViewItem { } this.mouseUpTimeout = setTimeout(() => { - dom.removeClass(this.container, 'clicked'); + this.container.classList.remove('clicked'); }, 800); // delayed to prevent focus feedback from showing on mouse up })); @@ -328,7 +328,7 @@ export class ActivityActionViewItem extends BaseActionViewItem { if (this.options.icon && !this.activity.iconUrl) { // Only apply codicon class to activity bar icon items without iconUrl - dom.addClass(this.label, 'codicon'); + this.label.classList.add('codicon'); } if (!this.options.icon) { @@ -588,10 +588,10 @@ export class CompositeActionViewItem extends ActivityActionViewItem { const left = forceLeft || (preferLeft && !lastClasses.horizontal) || (!forceRight && lastClasses.horizontal === 'left'); const right = forceRight || (!preferLeft && !lastClasses.horizontal) || (!forceLeft && lastClasses.horizontal === 'right'); - dom.toggleClass(element, 'top', showFeedback && top); - dom.toggleClass(element, 'bottom', showFeedback && bottom); - dom.toggleClass(element, 'left', showFeedback && left); - dom.toggleClass(element, 'right', showFeedback && right); + element.classList.toggle('top', showFeedback && top); + element.classList.toggle('bottom', showFeedback && bottom); + element.classList.toggle('left', showFeedback && left); + element.classList.toggle('right', showFeedback && right); if (!showFeedback) { return undefined; @@ -646,11 +646,11 @@ export class CompositeActionViewItem extends ActivityActionViewItem { protected updateChecked(): void { if (this.getAction().checked) { - dom.addClass(this.container, 'checked'); + this.container.classList.add('checked'); this.container.setAttribute('aria-label', nls.localize('compositeActive', "{0} active", this.container.title)); this.container.setAttribute('aria-expanded', 'true'); } else { - dom.removeClass(this.container, 'checked'); + this.container.classList.remove('checked'); this.container.setAttribute('aria-label', this.container.title); this.container.setAttribute('aria-expanded', 'false'); } @@ -663,9 +663,9 @@ export class CompositeActionViewItem extends ActivityActionViewItem { } if (this.getAction().enabled) { - dom.removeClass(this.element, 'disabled'); + this.element.classList.remove('disabled'); } else { - dom.addClass(this.element, 'disabled'); + this.element.classList.add('disabled'); } } diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index b3c94885b7a..c9c0963fed8 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -7,7 +7,6 @@ import 'vs/css!./media/compositepart'; import * as nls from 'vs/nls'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { IDisposable, dispose, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; -import * as strings from 'vs/base/common/strings'; import { Emitter } from 'vs/base/common/event'; import * as errors from 'vs/base/common/errors'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; @@ -29,7 +28,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { Dimension, append, $, addClass, hide, show, addClasses } from 'vs/base/browser/dom'; +import { Dimension, append, $, hide, show, addClasses } from 'vs/base/browser/dom'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { assertIsDefined, withNullAsUndefined } from 'vs/base/common/types'; @@ -384,7 +383,7 @@ export abstract class CompositePart extends Part { // Title Area Container const titleArea = append(parent, $('.composite')); - addClass(titleArea, 'title'); + titleArea.classList.add('title'); // Left Title Label this.titleLabel = this.createTitleLabel(titleArea); @@ -413,8 +412,8 @@ export abstract class CompositePart extends Part { const $this = this; return { - updateTitle: (id, title, keybinding) => { - titleLabel.innerHTML = strings.escape(title); + updateTitle: (_id, title, keybinding) => { + titleLabel.innerText = title; titleLabel.title = keybinding ? nls.localize('titleTooltip', "{0} ({1})", title, keybinding) : title; }, diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 1f168f323af..3473bb05dee 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -7,7 +7,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import * as nls from 'vs/nls'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; -import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, TextCompareEditorActiveContext, EditorPinnedContext, EditorGroupEditorsCountContext, EditorStickyContext, ActiveEditorAvailableEditorIdsContext } from 'vs/workbench/common/editor'; +import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, TextCompareEditorActiveContext, EditorPinnedContext, EditorGroupEditorsCountContext, EditorStickyContext, ActiveEditorAvailableEditorIdsContext, MultipleEditorGroupsContext } from 'vs/workbench/common/editor'; import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; @@ -525,7 +525,7 @@ appendEditorToolItem( title: nls.localize('close', "Close"), icon: { id: 'codicon/close' } }, - ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.not('groupActiveEditorDirty')), + ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.not('activeEditorIsDirty')), 1000000, // towards the far end { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, @@ -540,7 +540,7 @@ appendEditorToolItem( title: nls.localize('close', "Close"), icon: { id: 'codicon/close-dirty' } }, - ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.has('groupActiveEditorDirty')), + ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.has('activeEditorIsDirty')), 1000000, // towards the far end { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, @@ -602,6 +602,7 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorComman MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: { value: nls.localize('closeSavedEditors', "Close Saved Editors in Group"), original: 'Close Saved Editors in Group' }, category: viewCategory } }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: { value: nls.localize('closeOtherEditors', "Close Other Editors in Group"), original: 'Close Other Editors in Group' }, category: viewCategory } }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: { value: nls.localize('closeRightEditors', "Close Editors to the Right in Group"), original: 'Close Editors to the Right in Group' }, category: viewCategory } }); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_AND_GROUP_COMMAND_ID, title: { value: nls.localize('closeEditorGroup', "Close Editor Group"), original: 'Close Editor Group' }, category: viewCategory }, when: MultipleEditorGroupsContext }); // File menu MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 0fcb9999b13..2f4a553bc0a 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/editorgroupview'; import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup'; -import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditorPane, EditorGroupEditorsCountContext, SaveReason, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane, EditorStickyContext, EditorPinnedContext } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorDirtyContext, IEditorPane, EditorGroupEditorsCountContext, SaveReason, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane, EditorStickyContext, EditorPinnedContext, Deprecated_EditorPinnedContext, Deprecated_EditorDirtyContext } from 'vs/workbench/common/editor'; import { Event, Emitter, Relay } from 'vs/base/common/event'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { addClass, addClasses, Dimension, trackFocus, toggleClass, removeClass, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor } from 'vs/base/browser/dom'; @@ -217,10 +217,12 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } private handleGroupContextKeys(contextKeyService: IContextKeyService): void { - const groupActiveEditorDirtyContextKey = EditorGroupActiveEditorDirtyContext.bindTo(contextKeyService); - const groupEditorsCountContext = EditorGroupEditorsCountContext.bindTo(contextKeyService); + const groupActiveEditorDirtyContext = EditorDirtyContext.bindTo(contextKeyService); + const deprecatedGroupActiveEditorDirtyContext = Deprecated_EditorDirtyContext.bindTo(contextKeyService); const groupActiveEditorPinnedContext = EditorPinnedContext.bindTo(contextKeyService); + const deprecatedGroupActiveEditorPinnedContext = Deprecated_EditorPinnedContext.bindTo(contextKeyService); const groupActiveEditorStickyContext = EditorStickyContext.bindTo(contextKeyService); + const groupEditorsCountContext = EditorGroupEditorsCountContext.bindTo(contextKeyService); const activeEditorListener = new MutableDisposable(); @@ -229,10 +231,15 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const activeEditor = this._group.activeEditor; if (activeEditor) { - groupActiveEditorDirtyContextKey.set(activeEditor.isDirty() && !activeEditor.isSaving()); - activeEditorListener.value = activeEditor.onDidChangeDirty(() => groupActiveEditorDirtyContextKey.set(activeEditor.isDirty() && !activeEditor.isSaving())); + groupActiveEditorDirtyContext.set(activeEditor.isDirty() && !activeEditor.isSaving()); + deprecatedGroupActiveEditorDirtyContext.set(activeEditor.isDirty() && !activeEditor.isSaving()); + activeEditorListener.value = activeEditor.onDidChangeDirty(() => { + groupActiveEditorDirtyContext.set(activeEditor.isDirty() && !activeEditor.isSaving()); + deprecatedGroupActiveEditorDirtyContext.set(activeEditor.isDirty() && !activeEditor.isSaving()); + }); } else { - groupActiveEditorDirtyContextKey.set(false); + groupActiveEditorDirtyContext.set(false); + deprecatedGroupActiveEditorDirtyContext.set(false); } }; @@ -247,6 +254,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { case GroupChangeKind.EDITOR_PIN: if (e.editor && e.editor === this._group.activeEditor) { groupActiveEditorPinnedContext.set(this._group.isPinned(this._group.activeEditor)); + deprecatedGroupActiveEditorPinnedContext.set(this._group.isPinned(this._group.activeEditor)); } break; case GroupChangeKind.EDITOR_STICKY: diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index ed99e7e0567..bfbb9589777 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -68,7 +68,7 @@ export class SideBySideEditor extends EditorPane { } protected createEditor(parent: HTMLElement): void { - DOM.addClass(parent, 'side-by-side-editor'); + parent.classList.add('side-by-side-editor'); const splitview = this.splitview = this._register(new SplitView(parent, { orientation: Orientation.HORIZONTAL })); this._register(this.splitview.onDidSashReset(() => splitview.distributeViewSizes())); diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index f3eb689c73a..a1f2ceec6be 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -24,7 +24,7 @@ import { IDisposable, dispose, DisposableStore, combinedDisposable, MutableDispo import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { getOrSet } from 'vs/base/common/map'; -import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BACKGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER, TAB_HOVER_BACKGROUND, TAB_HOVER_BORDER, TAB_UNFOCUSED_HOVER_BACKGROUND, TAB_UNFOCUSED_HOVER_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, WORKBENCH_BACKGROUND, TAB_ACTIVE_BORDER_TOP, TAB_UNFOCUSED_ACTIVE_BORDER_TOP, TAB_ACTIVE_MODIFIED_BORDER, TAB_INACTIVE_MODIFIED_BORDER, TAB_UNFOCUSED_ACTIVE_MODIFIED_BORDER, TAB_UNFOCUSED_INACTIVE_MODIFIED_BORDER, TAB_UNFOCUSED_INACTIVE_BACKGROUND, TAB_HOVER_FOREGROUND, TAB_UNFOCUSED_HOVER_FOREGROUND, EDITOR_GROUP_HEADER_TABS_BORDER } from 'vs/workbench/common/theme'; import { activeContrastBorder, contrastBorder, editorBackground, breadcrumbsBackground } from 'vs/platform/theme/common/colorRegistry'; import { ResourcesDropHandler, DraggedEditorIdentifier, DraggedEditorGroupIdentifier, DragAndDropObserver } from 'vs/workbench/browser/dnd'; @@ -45,6 +45,7 @@ import { basenameOrAuthority } from 'vs/base/common/resources'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IPath, win32, posix } from 'vs/base/common/path'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; interface IEditorInputLabel { name?: string; @@ -1464,7 +1465,7 @@ export class TabsTitleControl extends TitleControl { registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { // Add border between tabs and breadcrumbs in high contrast mode. - if (theme.type === HIGH_CONTRAST) { + if (theme.type === ColorScheme.HIGH_CONTRAST) { const borderColor = (theme.getColor(TAB_BORDER) || theme.getColor(contrastBorder)); collector.addRule(` .monaco-workbench .part.editor > .content .editor-group-container > .title.tabs > .tabs-and-actions-container { diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 84420014b15..e7480b64cec 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -26,7 +26,7 @@ import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbe import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions'; import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { Dimension, trackFocus, addClass, toggleClass, EventHelper } from 'vs/base/browser/dom'; +import { Dimension, trackFocus, EventHelper } from 'vs/base/browser/dom'; import { localize } from 'vs/nls'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -422,10 +422,10 @@ export class PanelPart extends CompositePart implements IPanelService { private createEmptyPanelMessage(): void { const contentArea = this.getContentArea()!; this.emptyPanelMessageElement = document.createElement('div'); - addClass(this.emptyPanelMessageElement, 'empty-panel-message-area'); + this.emptyPanelMessageElement.classList.add('empty-panel-message-area'); const messageElement = document.createElement('div'); - addClass(messageElement, 'empty-panel-message'); + messageElement.classList.add('empty-panel-message'); messageElement.innerText = localize('panel.emptyMessage', "Drag a view into the panel to display."); this.emptyPanelMessageElement.appendChild(messageElement); @@ -613,7 +613,7 @@ export class PanelPart extends CompositePart implements IPanelService { private emptyPanelMessageElement: HTMLElement | undefined; private layoutEmptyMessage(): void { if (this.emptyPanelMessageElement) { - toggleClass(this.emptyPanelMessageElement, 'visible', this.compositeBar.getVisibleComposites().length === 0); + this.emptyPanelMessageElement.classList.toggle('visible', this.compositeBar.getVisibleComposites().length === 0); } } diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index e29be7ab5a0..aeca6388f8b 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -15,7 +15,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntry, IStatusbarEntryAccessor } from 'vs/workbench/services/statusbar/common/statusbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { Action, IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, Separator } from 'vs/base/common/actions'; -import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector, ThemeColor, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector, ThemeColor } from 'vs/platform/theme/common/themeService'; import { STATUS_BAR_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_ITEM_HOVER_BACKGROUND, STATUS_BAR_ITEM_ACTIVE_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND, STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND, STATUS_BAR_BORDER, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER } from 'vs/workbench/common/theme'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; @@ -38,6 +38,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; interface IPendingStatusbarEntry { id: string; @@ -672,7 +673,7 @@ export class StatusbarPart extends Part implements IStatusbarService { this.styleElement = createStyleSheet(container); } - this.styleElement.innerHTML = `.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor}; }`; + this.styleElement.textContent = `.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor}; }`; } private doCreateStatusItem(id: string, alignment: StatusbarAlignment, ...extraClasses: string[]): HTMLElement { @@ -889,7 +890,7 @@ class StatusbarEntryItem extends Disposable { } registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { - if (theme.type !== HIGH_CONTRAST) { + if (theme.type !== ColorScheme.HIGH_CONTRAST) { const statusBarItemHoverBackground = theme.getColor(STATUS_BAR_ITEM_HOVER_BACKGROUND); if (statusBarItemHoverBackground) { collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item a:hover { background-color: ${statusBarItemHoverBackground}; }`); diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index d718c7846ca..07c156043db 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -9,9 +9,8 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ColorIdentifier, activeContrastBorder, foreground } from 'vs/platform/theme/common/colorRegistry'; import { attachStyler, IColorMapping, attachButtonStyler, attachLinkStyler, attachProgressBarStyler } 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, PANEL_BACKGROUND, SIDE_BAR_BACKGROUND, PANEL_SECTION_HEADER_FOREGROUND, PANEL_SECTION_HEADER_BACKGROUND, PANEL_SECTION_HEADER_BORDER, PANEL_SECTION_DRAG_AND_DROP_BACKGROUND, PANEL_SECTION_BORDER } from 'vs/workbench/common/theme'; -import { after, append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener, removeClass, addClass, createCSSRule, asCSSUrl, addClasses } from 'vs/base/browser/dom'; +import { after, append, $, trackFocus, EventType, isAncestor, Dimension, addDisposableListener, createCSSRule, asCSSUrl, addClasses } from 'vs/base/browser/dom'; import { IDisposable, combinedDisposable, dispose, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { firstIndex } from 'vs/base/common/arrays'; import { IAction, Separator, IActionViewItem } from 'vs/base/common/actions'; import { ActionsOrientation, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -238,7 +237,7 @@ export abstract class ViewPane extends Pane implements IView { set headerVisible(visible: boolean) { super.headerVisible = visible; - toggleClass(this.element, 'merged-header', !visible); + this.element.classList.toggle('merged-header', !visible); } setVisible(visible: boolean): void { @@ -294,7 +293,7 @@ export abstract class ViewPane extends Pane implements IView { this.renderHeaderTitle(container, this.title); const actions = append(container, $('.actions')); - toggleClass(actions, 'show', this.showActionsAlways); + actions.classList.toggle('show', this.showActionsAlways); this.toolbar = new ToolBar(actions, this.contextMenuService, { orientation: ActionsOrientation.HORIZONTAL, actionViewItemProvider: action => this.getActionViewItem(action), @@ -357,7 +356,7 @@ export abstract class ViewPane extends Pane implements IView { -webkit-mask-size: 16px; `); } else if (isString(icon)) { - addClass(this.iconContainer, 'codicon'); + this.iconContainer.classList.add('codicon'); cssClass = icon; } @@ -489,7 +488,7 @@ export abstract class ViewPane extends Pane implements IView { return; } const shouldAlwaysShowActions = this.configurationService.getValue('workbench.view.alwaysShowHeaderActions'); - toggleClass(this.headerContainer, 'actions-always-visible', shouldAlwaysShowActions); + this.headerContainer.classList.toggle('actions-always-visible', shouldAlwaysShowActions); } protected updateActions(): void { @@ -534,7 +533,7 @@ export abstract class ViewPane extends Pane implements IView { this.viewWelcomeDisposable.dispose(); if (!this.shouldShowWelcome()) { - removeClass(this.bodyContainer, 'welcome'); + this.bodyContainer.classList.remove('welcome'); this.viewWelcomeContainer.innerText = ''; this.scrollableElement.scanDomNode(); return; @@ -543,14 +542,14 @@ export abstract class ViewPane extends Pane implements IView { const contents = this.viewWelcomeController.contents; if (contents.length === 0) { - removeClass(this.bodyContainer, 'welcome'); + this.bodyContainer.classList.remove('welcome'); this.viewWelcomeContainer.innerText = ''; this.scrollableElement.scanDomNode(); return; } const disposables = new DisposableStore(); - addClass(this.bodyContainer, 'welcome'); + this.bodyContainer.classList.add('welcome'); this.viewWelcomeContainer.innerText = ''; let buttonIndex = 0; @@ -680,15 +679,15 @@ class ViewPaneDropOverlay extends Themable { // Parent this.paneElement.appendChild(this.container); - addClass(this.paneElement, 'dragged-over'); + this.paneElement.classList.add('dragged-over'); this._register(toDisposable(() => { this.paneElement.removeChild(this.container); - removeClass(this.paneElement, 'dragged-over'); + this.paneElement.classList.remove('dragged-over'); })); // Overlay this.overlay = document.createElement('div'); - addClass(this.overlay, 'pane-overlay-indicator'); + this.overlay.classList.add('pane-overlay-indicator'); this.container.appendChild(this.overlay); // Overlay Event Handling @@ -819,7 +818,7 @@ class ViewPaneDropOverlay extends Themable { this.overlay.style.opacity = '1'; // Enable transition after a timeout to prevent initial animation - setTimeout(() => addClass(this.overlay, 'overlay-move-transition'), 0); + setTimeout(() => this.overlay.classList.add('overlay-move-transition'), 0); // Remember as current split direction this._currentDropOperation = dropDirection; @@ -1581,7 +1580,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { } private removePane(pane: ViewPane): void { - const index = firstIndex(this.paneItems, i => i.pane === pane); + const index = this.paneItems.findIndex(i => i.pane === pane); if (index === -1) { return; @@ -1598,8 +1597,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { } movePane(from: ViewPane, to: ViewPane): void { - const fromIndex = firstIndex(this.paneItems, item => item.pane === from); - const toIndex = firstIndex(this.paneItems, item => item.pane === to); + const fromIndex = this.paneItems.findIndex(item => item.pane === from); + const toIndex = this.paneItems.findIndex(item => item.pane === to); const fromViewDescriptor = this.viewContainerModel.visibleViewDescriptors[fromIndex]; const toViewDescriptor = this.viewContainerModel.visibleViewDescriptors[toIndex]; diff --git a/src/vs/workbench/browser/style.ts b/src/vs/workbench/browser/style.ts index d8a43a7ace3..c562af3bb9f 100644 --- a/src/vs/workbench/browser/style.ts +++ b/src/vs/workbench/browser/style.ts @@ -5,12 +5,13 @@ import 'vs/css!./media/style'; -import { registerThemingParticipant, IColorTheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { iconForeground, foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; import { WORKBENCH_BACKGROUND, TITLE_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme'; import { isWeb, isIOS, isMacintosh, isWindows } from 'vs/base/common/platform'; import { createMetaElement } from 'vs/base/browser/dom'; import { isSafari, isStandalone } from 'vs/base/browser/browser'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { @@ -127,7 +128,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = } // High Contrast theme overwrites for outline - if (theme.type === HIGH_CONTRAST) { + if (theme.type === ColorScheme.HIGH_CONTRAST) { collector.addRule(` .hc-black [tabindex="0"]:focus, .hc-black [tabindex="-1"]:focus, diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 84f8ce721ad..8a59d65aac2 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -24,23 +24,34 @@ import { IResourceEditorInputType } from 'vs/workbench/services/editor/common/ed import { IRange } from 'vs/editor/common/core/range'; import { IExtUri } from 'vs/base/common/resources'; -export const DirtyWorkingCopiesContext = new RawContextKey('dirtyWorkingCopies', false); +// Editor State Context Keys +export const EditorDirtyContext = new RawContextKey('activeEditorIsDirty', false); +export const EditorPinnedContext = new RawContextKey('activeEditorIsNotPreview', false); +export const EditorStickyContext = new RawContextKey('activeEditorIsPinned', false); +export const EditorReadonlyContext = new RawContextKey('activeEditorIsReadonly', false); + +/** TODO@ben remove me eventually */ +/** @deprecated */ +export const Deprecated_EditorPinnedContext = new RawContextKey('editorPinned', false); +/** @deprecated */ +export const Deprecated_EditorDirtyContext = new RawContextKey('groupActiveEditorDirty', false); + +// Editor Kind Context Keys export const ActiveEditorContext = new RawContextKey('activeEditor', null); -export const ActiveEditorIsReadonlyContext = new RawContextKey('activeEditorIsReadonly', false); export const ActiveEditorAvailableEditorIdsContext = new RawContextKey('activeEditorAvailableEditorIds', ''); -export const EditorsVisibleContext = new RawContextKey('editorIsOpen', false); -export const EditorPinnedContext = new RawContextKey('editorPinned', false); -export const EditorStickyContext = new RawContextKey('editorSticky', false); -export const EditorGroupActiveEditorDirtyContext = new RawContextKey('groupActiveEditorDirty', false); -export const EditorGroupEditorsCountContext = new RawContextKey('groupEditorsCount', 0); -export const NoEditorsVisibleContext = EditorsVisibleContext.toNegated(); export const TextCompareEditorVisibleContext = new RawContextKey('textCompareEditorVisible', false); export const TextCompareEditorActiveContext = new RawContextKey('textCompareEditorActive', false); + +// Editor Group Context Keys +export const EditorGroupEditorsCountContext = new RawContextKey('groupEditorsCount', 0); export const ActiveEditorGroupEmptyContext = new RawContextKey('activeEditorGroupEmpty', false); export const ActiveEditorGroupIndexContext = new RawContextKey('activeEditorGroupIndex', 0); export const ActiveEditorGroupLastContext = new RawContextKey('activeEditorGroupLast', false); export const MultipleEditorGroupsContext = new RawContextKey('multipleEditorGroups', false); export const SingleEditorGroupsContext = MultipleEditorGroupsContext.toNegated(); + +// Editor Layout Context Keys +export const EditorsVisibleContext = new RawContextKey('editorIsOpen', false); export const InEditorZenModeContext = new RawContextKey('inZenMode', false); export const IsCenteredLayoutContext = new RawContextKey('isCenteredLayout', false); export const SplitEditorsVertically = new RawContextKey('splitEditorsVertically', false); diff --git a/src/vs/workbench/common/editor/textResourceEditorInput.ts b/src/vs/workbench/common/editor/textResourceEditorInput.ts index 92bb60db4ad..40a50a7c44b 100644 --- a/src/vs/workbench/common/editor/textResourceEditorInput.ts +++ b/src/vs/workbench/common/editor/textResourceEditorInput.ts @@ -137,7 +137,10 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput { } isUntitled(): boolean { - return this.resource.scheme === Schemas.untitled; + // anyFile: is never untitled as it can be saved + // untitled: is untitled by definition + // anyOther: is untitled because it cannot be saved, as such we expect a "Save As" dialog + return !this.fileService.canHandleResource(this.resource); } isReadonly(): boolean { @@ -161,6 +164,14 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput { } save(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { + + // If this is neither an `untitled` resource, nor a resource + // we can handle with the file service, we can only "Save As..." + if (this.resource.scheme !== Schemas.untitled && !this.fileService.canHandleResource(this.resource)) { + return this.saveAs(group, options); + } + + // Normal save return this.doSave(group, options, false); } diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index e50bd99f313..50a97ca01ed 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -10,7 +10,7 @@ import { RawContextKey, ContextKeyExpression } from 'vs/platform/contextkey/comm import { localize } from 'vs/nls'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { ThemeColor, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { getOrSet } from 'vs/base/common/map'; import { Registry } from 'vs/platform/registry/common/platform'; import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -644,6 +644,8 @@ export interface ITreeItem { themeIcon?: ThemeIcon; + iconColor?: ThemeColor; + resourceUri?: UriComponents; tooltip?: string | IMarkdownString; @@ -666,6 +668,7 @@ export class ResolvableTreeItem implements ITreeItem { icon?: UriComponents; iconDark?: UriComponents; themeIcon?: ThemeIcon; + iconColor?: ThemeColor; resourceUri?: UriComponents; tooltip?: string | IMarkdownString; contextValue?: string; diff --git a/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts b/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts index e895bd00e60..996eddc5b18 100644 --- a/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts +++ b/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts @@ -12,7 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { assertType } from 'vs/base/common/types'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -180,7 +180,7 @@ CommandsRegistry.registerCommand('_executePrepareCallHierarchy', async (accessor return [model.root]; } finally { - dispose(textModelReference); + textModelReference?.dispose(); } }); diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts index 16f632117f0..75138d4a33c 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts @@ -20,7 +20,7 @@ import { FontStyle, LanguageIdentifier, StandardTokenType, TokenMetadata, Docume 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'; -import { HIGH_CONTRAST, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; 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'; @@ -29,6 +29,7 @@ import { ColorThemeData, TokenStyleDefinitions, TokenStyleDefinition, TextMateTh import { SemanticTokenRule, TokenStyleData, TokenStyle } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { SEMANTIC_HIGHLIGHTING_SETTING_ID, IEditorSemanticHighlightingOptions } from 'vs/editor/common/services/modelServiceImpl'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; const $ = dom.$; @@ -643,7 +644,7 @@ registerEditorAction(InspectEditorTokens); registerThemingParticipant((theme, collector) => { const border = theme.getColor(editorHoverBorder); if (border) { - let borderWidth = theme.type === HIGH_CONTRAST ? 2 : 1; + let borderWidth = theme.type === ColorScheme.HIGH_CONTRAST ? 2 : 1; collector.addRule(`.monaco-editor .token-inspect-widget { border: ${borderWidth}px solid ${border}; }`); collector.addRule(`.monaco-editor .token-inspect-widget .tiw-metadata-separator { background-color: ${border}; }`); } diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index 4b3c2a72a1c..774e0b74dde 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -11,7 +11,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { SelectBox, ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IDebugService, IDebugSession, IDebugConfiguration, IConfig, ILaunch } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IDebugSession, IDebugConfiguration, IConfig, ILaunch, IDebugConfigurationProvider } from 'vs/workbench/contrib/debug/common/debug'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { selectBorder, selectBackground } from 'vs/platform/theme/common/colorRegistry'; @@ -34,7 +34,7 @@ export class StartDebugActionViewItem implements IActionViewItem { private options: { label: string, handler: (() => Promise) }[] = []; private toDispose: IDisposable[]; private selected = 0; - private providers: { label: string, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[] = []; + private providers: { label: string, provider: IDebugConfigurationProvider, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[] = []; constructor( private context: unknown, @@ -182,7 +182,7 @@ export class StartDebugActionViewItem implements IActionViewItem { const label = inWorkspace ? `${name} (${launch.name})` : name; this.options.push({ label, handler: async () => { - manager.selectConfiguration(launch, name); + await manager.selectConfiguration(launch, name); return true; } }); @@ -196,7 +196,7 @@ export class StartDebugActionViewItem implements IActionViewItem { } this.providers.forEach(p => { - if (p.label === manager.selectedConfiguration.name) { + if (p.provider.type === manager.selectedConfiguration.config?.type) { this.selected = this.options.length; } @@ -204,7 +204,7 @@ export class StartDebugActionViewItem implements IActionViewItem { label: `${p.label}...`, handler: async () => { const picked = await p.pick(); if (picked) { - manager.selectConfiguration(picked.launch, p.label, picked.config); + await manager.selectConfiguration(picked.launch, picked.config.name, picked.config); return true; } return false; diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index e28eaf83505..7cc46462c3f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -46,6 +46,8 @@ jsonRegistry.registerSchema(launchSchemaId, launchSchema); const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname'; const DEBUG_SELECTED_ROOT = 'debug.selectedroot'; +// Debug type is only stored if a dynamic configuration is used for better restore +const DEBUG_SELECTED_TYPE = 'debug.selectedtype'; interface IDynamicPickItem { label: string, launch: ILaunch, config: IConfig } @@ -84,13 +86,15 @@ export class ConfigurationManager implements IConfigurationManager { this.initLaunches(); this.registerListeners(); const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); + const previousSelectedType = this.storageService.get(DEBUG_SELECTED_TYPE, StorageScope.WORKSPACE); const previousSelectedLaunch = this.launches.find(l => l.uri.toString() === previousSelectedRoot); + const previousSelectedName = this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE); this.debugConfigurationTypeContext = CONTEXT_DEBUG_CONFIGURATION_TYPE.bindTo(contextKeyService); this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService); if (previousSelectedLaunch && previousSelectedLaunch.getConfigurationNames().length) { - this.selectConfiguration(previousSelectedLaunch, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE)); + this.selectConfiguration(previousSelectedLaunch, previousSelectedName, undefined, previousSelectedType); } else if (this.launches.length > 0) { - this.selectConfiguration(undefined); + this.selectConfiguration(undefined, previousSelectedName, undefined, previousSelectedType); } } @@ -259,7 +263,7 @@ export class ConfigurationManager implements IConfigurationManager { return results.reduce((first, second) => first.concat(second), []); } - async getDynamicProviders(): Promise<{ label: string, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]> { + async getDynamicProviders(): Promise<{ label: string, provider: IDebugConfigurationProvider, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]> { const extensions = await this.extensionService.getExtensions(); const onDebugDynamicConfigurationsName = 'onDebugDynamicConfigurations'; const debugDynamicExtensionsTypes = extensions.reduce((acc, e) => { @@ -289,9 +293,12 @@ export class ConfigurationManager implements IConfigurationManager { return acc; }, [] as string[]); + await Promise.all(debugDynamicExtensionsTypes.map(type => this.activateDebuggers(onDebugDynamicConfigurationsName, type))); return debugDynamicExtensionsTypes.map(type => { + const provider = this.configProviders.find(p => p.type === type && p.triggerKind === DebugConfigurationProviderTriggerKind.Dynamic && p.provideDebugConfigurations)!; return { label: this.getDebuggerLabel(type)!, + provider, pick: async () => { const disposables = new DisposableStore(); const input = disposables.add(this.quickInputService.createQuickPick()); @@ -308,15 +315,13 @@ export class ConfigurationManager implements IConfigurationManager { await launch.openConfigFile(false, config.type); // Only Launch have a pin trigger button await (launch as Launch).writeConfiguration(config); - this.selectConfiguration(launch, config.name); + await this.selectConfiguration(launch, config.name); })); disposables.add(input.onDidHide(() => { chosenDidCancel = true; resolve(); })); }); - await this.activateDebuggers(onDebugDynamicConfigurationsName, type); const token = new CancellationTokenSource(); const picks: Promise[] = []; - const provider = this.configProviders.filter(p => p.type === type && p.triggerKind === DebugConfigurationProviderTriggerKind.Dynamic && p.provideDebugConfigurations)[0]; this.getLaunches().forEach(launch => { if (launch.workspace && provider) { picks.push(provider.provideDebugConfigurations!(launch.workspace.uri, token.token).then(configurations => configurations.map(config => ({ @@ -508,7 +513,7 @@ export class ConfigurationManager implements IConfigurationManager { return undefined; } - selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig): void { + async selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, type?: string): Promise { if (typeof launch === 'undefined') { const rootUri = this.historyService.getLastActiveWorkspaceRoot(); launch = this.getLaunch(rootUri); @@ -526,14 +531,31 @@ export class ConfigurationManager implements IConfigurationManager { } else { this.storageService.remove(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); } + const names = launch ? launch.getConfigurationNames() : []; if ((name && names.indexOf(name) >= 0) || config) { this.setSelectedLaunchName(name); } else if (!this.selectedName || names.indexOf(this.selectedName) === -1) { - this.setSelectedLaunchName(names.length ? names[0] : undefined); + // We could not find the previously used name. We should get all dynamic configurations from providers + // And potentially auto select the previously used dynamic configuration #96293 + const providers = await this.getDynamicProviders(); + const provider = providers.find(p => p.provider.type === type); + let nameToSet = names.length ? names[0] : undefined; + if (provider && launch && launch.workspace) { + const token = new CancellationTokenSource(); + const dynamicConfigs = await provider.provider.provideDebugConfigurations!(launch.workspace.uri, token.token); + const dynamicConfig = dynamicConfigs.find(c => c.name === name); + if (dynamicConfig) { + config = dynamicConfig; + nameToSet = name; + } + } + + this.setSelectedLaunchName(nameToSet); } this.selectedConfig = config; + this.storageService.store(DEBUG_SELECTED_TYPE, this.selectedConfig?.type, StorageScope.WORKSPACE); const configForType = this.selectedConfig || (this.selectedLaunch && this.selectedName ? this.selectedLaunch.getConfiguration(this.selectedName) : undefined); if (configForType) { this.debugConfigurationTypeContext.set(configForType.type); diff --git a/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts b/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts index cec5357ecc9..b8aac7951fb 100644 --- a/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts +++ b/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts @@ -64,7 +64,7 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { - this.debugService.getConfigurationManager().selectConfiguration(config.launch, config.name); + await this.debugService.getConfigurationManager().selectConfiguration(config.launch, config.name); try { await this.debugService.startDebugging(config.launch); } catch (error) { diff --git a/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts b/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts index 9316b53c8f4..51f6147c025 100644 --- a/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts +++ b/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts @@ -84,7 +84,7 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri this.styleElement = createStyleSheet(container); } - this.styleElement.innerHTML = `.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor} !important; }`; + this.styleElement.textContent = `.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor} !important; }`; } private getColorKey(noFolderColor: string, debuggingColor: string, normalColor: string): string { diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index fd43be0439c..7634f82926d 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -671,7 +671,7 @@ export interface IConfigurationManager { name: string | undefined; }; - selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig): void; + selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig): Promise; getLaunches(): ReadonlyArray; @@ -692,7 +692,7 @@ export interface IConfigurationManager { isDebuggerInterestedInLanguage(language: string): boolean; hasDebugConfigurationProvider(debugType: string): boolean; - getDynamicProviders(): Promise<{ label: string, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]>; + getDynamicProviders(): Promise<{ label: string, provider: IDebugConfigurationProvider, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]>; registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable; unregisterDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): void; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index b0198b8aab4..5f73348ed0b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -14,7 +14,7 @@ import { Action, IAction } from 'vs/base/common/actions'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { dispose, toDisposable, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { domEvent } from 'vs/base/browser/event'; -import { append, $, addClass, removeClass, finalHandler, join, toggleClass, hide, show, addDisposableListener, EventType } from 'vs/base/browser/dom'; +import { append, $, finalHandler, join, hide, show, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -37,7 +37,6 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Color } from 'vs/base/common/color'; -import { assign } from 'vs/base/common/objects'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionsTree, ExtensionData, ExtensionsGridView, getExtensions } from 'vs/workbench/contrib/extensions/browser/extensionsViewer'; @@ -361,11 +360,11 @@ export class ExtensionEditor extends EditorPane { ] } */ - this.telemetryService.publicLog('extensionGallery:openExtension', assign(extension.telemetryData, recommendationsData)); + this.telemetryService.publicLog('extensionGallery:openExtension', { ...extension.telemetryData, ...recommendationsData }); - toggleClass(template.name, 'clickable', !!extension.url); - toggleClass(template.publisher, 'clickable', !!extension.url); - toggleClass(template.rating, 'clickable', !!extension.url); + template.name.classList.toggle('clickable', !!extension.url); + template.publisher.classList.toggle('clickable', !!extension.url); + template.rating.classList.toggle('clickable', !!extension.url); if (extension.url) { this.transientDisposables.add(this.onClick(template.name, () => this.openerService.open(URI.parse(extension.url!)))); this.transientDisposables.add(this.onClick(template.rating, () => this.openerService.open(URI.parse(`${extension.url}#review-details`)))); @@ -556,7 +555,7 @@ export class ExtensionEditor extends EditorPane { ] } */ - this.telemetryService.publicLog('extensionEditor:navbarChange', assign(extension.telemetryData, { navItem: id })); + this.telemetryService.publicLog('extensionEditor:navbarChange', { ...extension.telemetryData, navItem: id }); } this.contentDisposables.clear(); @@ -848,13 +847,13 @@ export class ExtensionEditor extends EditorPane { const extensionPack = append(extensionPackReadme, $('div', { class: 'extension-pack' })); if (manifest.extensionPack!.length <= 3) { - addClass(extensionPackReadme, 'one-row'); + extensionPackReadme.classList.add('one-row'); } else if (manifest.extensionPack!.length <= 6) { - addClass(extensionPackReadme, 'two-rows'); + extensionPackReadme.classList.add('two-rows'); } else if (manifest.extensionPack!.length <= 9) { - addClass(extensionPackReadme, 'three-rows'); + extensionPackReadme.classList.add('three-rows'); } else { - addClass(extensionPackReadme, 'more-rows'); + extensionPackReadme.classList.add('more-rows'); } const extensionPackHeader = append(extensionPack, $('div.header')); @@ -1437,10 +1436,10 @@ export class ExtensionEditor extends EditorPane { } private loadContents(loadingTask: () => CacheResult, template: IExtensionEditorTemplate): Promise { - addClass(template.content, 'loading'); + template.content.classList.add('loading'); const result = this.contentDisposables.add(loadingTask()); - const onDone = () => removeClass(template.content, 'loading'); + const onDone = () => template.content.classList.remove('loading'); result.promise.then(onDone, onDone); return result.promise; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index dfc62721900..c3d8687c295 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -13,7 +13,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { distinct, shuffle } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; -import { assign } from 'vs/base/common/objects'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { DynamicWorkspaceRecommendations } from 'vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations'; @@ -260,7 +259,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte ] } */ - this.telemetryService.publicLog('extensionGallery:install:recommendations', assign(e.gallery.telemetryData, { recommendationReason: recommendationReason.reasonId })); + this.telemetryService.publicLog('extensionGallery:install:recommendations', { ...e.gallery.telemetryData, recommendationReason: recommendationReason.reasonId }); } } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 975786bc6f6..21e9118f568 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/extension'; -import { append, $, addClass, removeClass, toggleClass } from 'vs/base/browser/dom'; +import { append, $ } from 'vs/base/browser/dom'; import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle'; import { IAction } from 'vs/base/common/actions'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -135,7 +135,7 @@ export class Renderer implements IPagedRenderer { } renderPlaceholder(index: number, data: ITemplateData): void { - addClass(data.element, 'loading'); + data.element.classList.add('loading'); data.root.removeAttribute('aria-label'); data.root.removeAttribute('data-extension-id'); @@ -150,7 +150,7 @@ export class Renderer implements IPagedRenderer { } renderElement(extension: IExtension, index: number, data: ITemplateData): void { - removeClass(data.element, 'loading'); + data.element.classList.remove('loading'); data.root.setAttribute('data-extension-id', extension.identifier.id); if (extension.state !== ExtensionState.Uninstalled && !extension.server) { @@ -168,7 +168,7 @@ export class Renderer implements IPagedRenderer { const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, extension.identifier))[0]; isDisabled = !(runningExtension && extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension))); } - toggleClass(data.root, 'disabled', isDisabled); + data.root.classList.toggle('disabled', isDisabled); }; updateEnablement(); this.extensionService.onDidChangeExtensions(() => updateEnablement(), this, data.extensionDisposables); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts index 5ee60dcc409..d6436bc993f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts @@ -136,7 +136,7 @@ export class ExtensionRenderer implements IListRenderer('img.icon')); const details = dom.append(container, dom.$('.details')); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index a5221aff5e8..a03702b05d0 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -13,7 +13,7 @@ import { Event as EventOf, Emitter } from 'vs/base/common/event'; import { IAction, Action, Separator, SubmenuAction } from 'vs/base/common/actions'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { append, $, addClass, toggleClass, Dimension, hide, show } from 'vs/base/browser/dom'; +import { append, $, Dimension, hide, show } from 'vs/base/browser/dom'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -404,7 +404,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE } create(parent: HTMLElement): void { - addClass(parent, 'extensions-viewlet'); + parent.classList.add('extensions-viewlet'); this.root = parent; const overlay = append(this.root, $('.overlay')); @@ -500,7 +500,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE layout(dimension: Dimension): void { if (this.root) { - toggleClass(this.root, 'narrow', dimension.width <= 300); + this.root.classList.toggle('narrow', dimension.width <= 300); } if (this.searchBox) { this.searchBox.layout({ height: 20, width: dimension.width - 34 }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index becdf408944..2cc1ddbebc0 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -5,7 +5,6 @@ import { localize } from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; -import { assign } from 'vs/base/common/objects'; import { Event, Emitter } from 'vs/base/common/event'; import { isPromiseCanceledError, getErrorMessage } from 'vs/base/common/errors'; import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging'; @@ -14,7 +13,7 @@ import { IExtensionManagementServer, IExtensionManagementServerService, IExtensi import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { append, $, toggleClass, addClass } from 'vs/base/browser/dom'; +import { append, $ } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Delegate, Renderer, IExtensionsViewState } from 'vs/workbench/contrib/extensions/browser/extensionsList'; import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; @@ -121,7 +120,7 @@ export class ExtensionsListView extends ViewPane { } protected renderHeader(container: HTMLElement): void { - addClass(container, 'extension-view-header'); + container.classList.add('extension-view-header'); super.renderHeader(container); this.badge = new CountBadge(append(container, $('.count-badge-wrapper'))); @@ -198,10 +197,10 @@ export class ExtensionsListView extends ViewPane { }; switch (parsedQuery.sortBy) { - case 'installs': options = assign(options, { sortBy: SortBy.InstallCount }); break; - case 'rating': options = assign(options, { sortBy: SortBy.WeightedRating }); break; - case 'name': options = assign(options, { sortBy: SortBy.Title }); break; - case 'publishedDate': options = assign(options, { sortBy: SortBy.PublishedDate }); break; + case 'installs': options.sortBy = SortBy.InstallCount; break; + case 'rating': options.sortBy = SortBy.WeightedRating; break; + case 'name': options.sortBy = SortBy.Title; break; + case 'publishedDate': options.sortBy = SortBy.PublishedDate; break; } const successCallback = (model: IPagedModel) => { @@ -472,13 +471,15 @@ export class ExtensionsListView extends ViewPane { const text = query.value; if (/\bext:([^\s]+)\b/g.test(text)) { - options = assign(options, { text, source: 'file-extension-tags' }); + options.text = text; + options.source = 'file-extension-tags'; return this.extensionsWorkbenchService.queryGallery(options, token).then(pager => this.getPagedModel(pager)); } let preferredResults: string[] = []; if (text) { - options = assign(options, { text: text.substr(0, 350), source: 'searchText' }); + options.text = text.substr(0, 350); + options.source = 'searchText'; if (!hasUserDefinedSortOrder) { const searchExperiments = await this.getSearchExperiments(); for (const experiment of searchExperiments) { @@ -544,7 +545,9 @@ export class ExtensionsListView extends ViewPane { const names = await this.experimentService.getCuratedExtensionsList(value); if (Array.isArray(names) && names.length) { options.source = `curated:${value}`; - const pager = await this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }), token); + options.names = names; + options.pageSize = names.length; + const pager = await this.extensionsWorkbenchService.queryGallery(options, token); this.sortFirstPage(pager, names); return this.getPagedModel(pager || []); } @@ -701,8 +704,8 @@ export class ExtensionsListView extends ViewPane { if (this.bodyTemplate && this.badge) { - toggleClass(this.bodyTemplate.extensionsList, 'hidden', count === 0); - toggleClass(this.bodyTemplate.messageContainer, 'hidden', count > 0); + this.bodyTemplate.extensionsList.classList.toggle('hidden', count === 0); + this.bodyTemplate.messageContainer.classList.toggle('hidden', count > 0); this.badge.setCount(count); if (count === 0 && this.isBodyVisible()) { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index c5758eac3d6..63fcc8f02b2 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/extensionsWidgets'; import { Disposable, toDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { IExtension, IExtensionsWorkbenchService, IExtensionContainer } from 'vs/workbench/contrib/extensions/common/extensions'; -import { append, $, addClass, removeNode } from 'vs/base/browser/dom'; +import { append, $ } from 'vs/base/browser/dom'; import * as platform from 'vs/base/common/platform'; import { localize } from 'vs/nls'; import { IExtensionRecommendationsService, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -50,7 +50,7 @@ export class InstallCountWidget extends ExtensionWidget { @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService ) { super(); - addClass(container, 'extension-install-count'); + container.classList.add('extension-install-count'); this.render(); } @@ -95,10 +95,10 @@ export class RatingsWidget extends ExtensionWidget { private small: boolean ) { super(); - addClass(container, 'extension-ratings'); + container.classList.add('extension-ratings'); if (this.small) { - addClass(container, 'small'); + container.classList.add('small'); } this.render(); @@ -325,7 +325,7 @@ export class ExtensionPackCountWidget extends ExtensionWidget { private clear(): void { if (this.element) { - removeNode(this.element); + this.element.remove(); } } diff --git a/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts index f14ffc84eaf..9270ebca61f 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as arrays from 'vs/base/common/arrays'; import { localize } from 'vs/nls'; import { Event } from 'vs/base/common/event'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -44,7 +43,7 @@ export class KeymapExtensions extends Disposable implements IWorkbenchContributi private checkForOtherKeymaps(extensionIdentifier: IExtensionIdentifier): Promise { return this.instantiationService.invokeFunction(getInstalledExtensions).then(extensions => { const keymaps = extensions.filter(extension => isKeymapExtension(this.tipsService, extension)); - const extension = arrays.first(keymaps, extension => areSameExtensions(extension.identifier, extensionIdentifier)); + const extension = keymaps.find(extension => areSameExtensions(extension.identifier, extensionIdentifier)); if (extension && extension.globallyEnabled) { const otherKeymaps = keymaps.filter(extension => !areSameExtensions(extension.identifier, extensionIdentifier) && extension.globallyEnabled); if (otherKeymaps.length) { diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 56276cdec45..d34cf3bdd8a 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -17,7 +17,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IExtensionService, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; -import { append, $, reset, addClass, toggleClass, Dimension, clearNode } from 'vs/base/browser/dom'; +import { append, $, reset, Dimension, clearNode } from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -257,7 +257,7 @@ export class RuntimeExtensionsEditor extends EditorPane { } protected createEditor(parent: HTMLElement): void { - addClass(parent, 'runtime-extensions-editor'); + parent.classList.add('runtime-extensions-editor'); const TEMPLATE_ID = 'runtimeExtensionElementTemplate'; @@ -328,7 +328,7 @@ export class RuntimeExtensionsEditor extends EditorPane { data.elementDisposables = dispose(data.elementDisposables); - toggleClass(data.root, 'odd', index % 2 === 1); + data.root.classList.toggle('odd', index % 2 === 1); const onError = Event.once(domEvent(data.icon, 'error')); onError(() => data.icon.src = element.marketplaceInfo.iconUrlFallback, null, data.elementDisposables); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index 0ac3774a174..93146eae585 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -30,7 +30,6 @@ import { URI } from 'vs/base/common/uri'; import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IPager } from 'vs/base/common/paging'; -import { assign } from 'vs/base/common/objects'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ConfigurationKey, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; @@ -166,10 +165,9 @@ const noAssets: IGalleryExtensionAssets = { }; function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: IGalleryExtensionAssets = noAssets): IGalleryExtension { - const galleryExtension = Object.create({}); - assign(galleryExtension, { name, publisher: 'pub', version: '1.0.0', properties: {}, assets: {} }, properties); - assign(galleryExtension.properties, { dependencies: [] }, galleryExtensionProperties); - assign(galleryExtension.assets, assets); + const galleryExtension = Object.create({ name, publisher: 'pub', version: '1.0.0', properties: {}, assets: {}, ...properties }); + galleryExtension.properties = { ...galleryExtension.properties, dependencies: [], ...galleryExtensionProperties }; + galleryExtension.assets = { ...galleryExtension.assets, ...assets }; galleryExtension.identifier = { id: getGalleryExtensionId(galleryExtension.publisher, galleryExtension.name), uuid: uuid.generateUuid() }; return galleryExtension; } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index e2f71dede95..3f1897744af 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { assign } from 'vs/base/common/objects'; import { generateUuid } from 'vs/base/common/uuid'; import { IExtensionsWorkbenchService, ExtensionContainers } from 'vs/workbench/contrib/extensions/common/extensions'; import * as ExtensionsActions from 'vs/workbench/contrib/extensions/browser/extensionsActions'; @@ -2494,20 +2493,20 @@ suite('LocalInstallAction', () => { }); function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension { - manifest = assign({ name, publisher: 'pub', version: '1.0.0' }, manifest); - properties = assign({ + manifest = { name, publisher: 'pub', version: '1.0.0', ...manifest }; + properties = { type: ExtensionType.User, location: URI.file(`pub.${name}`), - identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name) } - }, properties); + identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name) }, + ...properties + }; return Object.create({ manifest, ...properties }); } function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: any = {}): IGalleryExtension { - const galleryExtension = Object.create({}); - assign(galleryExtension, { name, publisher: 'pub', version: '1.0.0', properties: {}, assets: {} }, properties); - assign(galleryExtension.properties, { dependencies: [] }, galleryExtensionProperties); - assign(galleryExtension.assets, assets); + const galleryExtension = Object.create({ name, publisher: 'pub', version: '1.0.0', properties: {}, assets: {}, ...properties }); + galleryExtension.properties = { ...galleryExtension.properties, dependencies: [], ...galleryExtensionProperties }; + galleryExtension.assets = { ...galleryExtension.assets, ...assets }; galleryExtension.identifier = { id: getGalleryExtensionId(galleryExtension.publisher, galleryExtension.name), uuid: generateUuid() }; return galleryExtension; } 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 e2a88db3452..7bcdce8cb9e 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 @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { assign } from 'vs/base/common/objects'; import { generateUuid } from 'vs/base/common/uuid'; import { ExtensionsListView } from 'vs/workbench/contrib/extensions/browser/extensionsViews'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; @@ -525,21 +524,21 @@ suite('ExtensionsListView Tests', () => { }); function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension { - manifest = assign({ name, publisher: 'pub', version: '1.0.0' }, manifest); - properties = assign({ + manifest = { name, publisher: 'pub', version: '1.0.0', ...manifest }; + properties = { type: ExtensionType.User, location: URI.file(`pub.${name}`), - identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, - metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' } - }, properties); + identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name) }, + metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' }, + ...properties + }; return Object.create({ manifest, ...properties }); } function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: any = {}): IGalleryExtension { - const galleryExtension = Object.create({}); - assign(galleryExtension, { name, publisher: 'pub', version: '1.0.0', properties: {}, assets: {} }, properties); - assign(galleryExtension.properties, { dependencies: [] }, galleryExtensionProperties); - assign(galleryExtension.assets, assets); + const galleryExtension = Object.create({ name, publisher: 'pub', version: '1.0.0', properties: {}, assets: {}, ...properties }); + galleryExtension.properties = { ...galleryExtension.properties, dependencies: [], ...galleryExtensionProperties }; + galleryExtension.assets = { ...galleryExtension.assets, ...assets }; galleryExtension.identifier = { id: getGalleryExtensionId(galleryExtension.publisher, galleryExtension.name), uuid: generateUuid() }; return galleryExtension; } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index b7f98de6930..15c3f1a0241 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -6,7 +6,6 @@ import * as sinon from 'sinon'; import * as assert from 'assert'; import * as fs from 'fs'; -import { assign } from 'vs/base/common/objects'; import { generateUuid } from 'vs/base/common/uuid'; import { IExtensionsWorkbenchService, ExtensionState, AutoCheckUpdatesConfigurationKey, AutoUpdateConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensions'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; @@ -1368,12 +1367,13 @@ suite('ExtensionsWorkbenchServiceTest', () => { } function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension { - manifest = assign({ name, publisher: 'pub', version: '1.0.0' }, manifest); - properties = assign({ + manifest = { name, publisher: 'pub', version: '1.0.0', ...manifest }; + properties = { type: ExtensionType.User, location: URI.file(`pub.${name}`), - identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name) } - }, properties); + identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name) }, + ...properties + }; return Object.create({ manifest, ...properties }); } @@ -1389,10 +1389,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { }; function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: IGalleryExtensionAssets = noAssets): IGalleryExtension { - const galleryExtension = Object.create({}); - assign(galleryExtension, { name, publisher: 'pub', version: '1.0.0', properties: {}, assets: {} }, properties); - assign(galleryExtension.properties, { dependencies: [] }, galleryExtensionProperties); - assign(galleryExtension.assets, assets); + const galleryExtension = Object.create({ name, publisher: 'pub', version: '1.0.0', properties: {}, assets: {}, ...properties }); + galleryExtension.properties = { ...galleryExtension.properties, dependencies: [], ...galleryExtensionProperties }; + galleryExtension.assets = { ...galleryExtension.assets, ...assets }; galleryExtension.identifier = { id: getGalleryExtensionId(galleryExtension.publisher, galleryExtension.name), uuid: generateUuid() }; return galleryExtension; } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index ac1df7b3a5c..26e04e608a9 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -10,7 +10,7 @@ import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTI import { SyncActionDescriptor, MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; -import { openWindowCommand, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, DirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, newWindowCommand, ReadonlyEditorContext, OPEN_WITH_EXPLORER_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID, NEW_UNTITLED_FILE_LABEL } from 'vs/workbench/contrib/files/browser/fileCommands'; +import { openWindowCommand, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, OpenEditorsDirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, newWindowCommand, OpenEditorsReadonlyEditorContext, OPEN_WITH_EXPLORER_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID, NEW_UNTITLED_FILE_LABEL } from 'vs/workbench/contrib/files/browser/fileCommands'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -22,11 +22,11 @@ import { AutoSaveAfterShortDelayContext } from 'vs/workbench/services/filesConfi import { ResourceContextKey } from 'vs/workbench/common/resources'; import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; import { Schemas } from 'vs/base/common/network'; -import { WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; +import { DirtyWorkingCopiesContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { OpenFileFolderAction, OpenFileAction, OpenFolderAction, OpenWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; -import { DirtyWorkingCopiesContext, ActiveEditorContext } from 'vs/workbench/common/editor'; +import { ActiveEditorContext } from 'vs/workbench/common/editor'; import { SidebarFocusContext } from 'vs/workbench/common/viewlet'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; @@ -259,7 +259,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { command: { id: SAVE_FILE_COMMAND_ID, title: SAVE_FILE_LABEL, - precondition: DirtyEditorContext + precondition: OpenEditorsDirtyEditorContext }, when: ContextKeyExpr.or( // Untitled Editors @@ -269,7 +269,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { // Not: editor groups OpenEditorsGroupContext.toNegated(), // Not: readonly editors - ReadonlyEditorContext.toNegated(), + OpenEditorsReadonlyEditorContext.toNegated(), // Not: auto save after short delay AutoSaveAfterShortDelayContext.toNegated() ) @@ -282,13 +282,13 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { command: { id: REVERT_FILE_COMMAND_ID, title: nls.localize('revert', "Revert File"), - precondition: DirtyEditorContext + precondition: OpenEditorsDirtyEditorContext }, when: ContextKeyExpr.and( // Not: editor groups OpenEditorsGroupContext.toNegated(), // Not: readonly editors - ReadonlyEditorContext.toNegated(), + OpenEditorsReadonlyEditorContext.toNegated(), // Not: untitled editors (revert closes them) ResourceContextKey.Scheme.notEqualsTo(Schemas.untitled), // Not: auto save after short delay @@ -314,7 +314,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { command: { id: COMPARE_WITH_SAVED_COMMAND_ID, title: nls.localize('compareWithSaved', "Compare with Saved"), - precondition: DirtyEditorContext + precondition: OpenEditorsDirtyEditorContext }, when: ContextKeyExpr.and(ResourceContextKey.IsFileSystemResource, AutoSaveAfterShortDelayContext.toNegated(), WorkbenchListDoubleSelection.toNegated()) }); @@ -428,7 +428,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { id: OPEN_WITH_EXPLORER_COMMAND_ID, title: nls.localize('explorerOpenWith', "Open With..."), }, - when: ContextKeyExpr.and(ExplorerRootContext.toNegated(), ExplorerResourceAvailableEditorIdsContext), + when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ExplorerResourceAvailableEditorIdsContext), }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index eae180f51c0..cf8f51deedc 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -74,8 +74,8 @@ export const SAVE_ALL_IN_GROUP_COMMAND_ID = 'workbench.files.action.saveAllInGro export const SAVE_FILES_COMMAND_ID = 'workbench.action.files.saveFiles'; export const OpenEditorsGroupContext = new RawContextKey('groupFocusedInOpenEditors', false); -export const DirtyEditorContext = new RawContextKey('dirtyEditor', false); -export const ReadonlyEditorContext = new RawContextKey('readonlyEditor', false); +export const OpenEditorsDirtyEditorContext = new RawContextKey('dirtyEditorFocusedInOpenEditors', false); +export const OpenEditorsReadonlyEditorContext = new RawContextKey('readonlyEditorFocusedInOpenEditors', false); export const ResourceSelectedForCompareContext = new RawContextKey('resourceSelectedForCompare', false); export const REMOVE_ROOT_FOLDER_COMMAND_ID = 'removeRootFolder'; diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index c35e3fa820b..07da7f744fd 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -811,8 +811,8 @@ export class ExplorerView extends ViewPane { } const newStyles = content.join('\n'); - if (newStyles !== this.styleElement.innerHTML) { - this.styleElement.innerHTML = newStyles; + if (newStyles !== this.styleElement.textContent) { + this.styleElement.textContent = newStyles; } } diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index e07fd997901..c7a308d3d65 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -31,7 +31,7 @@ import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; -import { DirtyEditorContext, OpenEditorsGroupContext, ReadonlyEditorContext } from 'vs/workbench/contrib/files/browser/fileCommands'; +import { OpenEditorsDirtyEditorContext, OpenEditorsGroupContext, OpenEditorsReadonlyEditorContext } from 'vs/workbench/contrib/files/browser/fileCommands'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { ResourcesDropHandler, fillResourceDataTransfers, CodeDataTransfers, containsDragType } from 'vs/workbench/browser/dnd'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -243,8 +243,8 @@ export class OpenEditorsView extends ViewPane { this.resourceContext = this.instantiationService.createInstance(ResourceContextKey); this._register(this.resourceContext); this.groupFocusedContext = OpenEditorsGroupContext.bindTo(this.contextKeyService); - this.dirtyEditorFocusedContext = DirtyEditorContext.bindTo(this.contextKeyService); - this.readonlyEditorFocusedContext = ReadonlyEditorContext.bindTo(this.contextKeyService); + this.dirtyEditorFocusedContext = OpenEditorsDirtyEditorContext.bindTo(this.contextKeyService); + this.readonlyEditorFocusedContext = OpenEditorsReadonlyEditorContext.bindTo(this.contextKeyService); this._register(this.list.onContextMenu(e => this.onListContextMenu(e))); this.list.onDidChangeFocus(e => { diff --git a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts index a935f264672..1304e665581 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts @@ -12,7 +12,6 @@ import { IJSONEditingService } from 'vs/workbench/services/configuration/common/ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { language } from 'vs/base/common/platform'; -import { firstIndex } from 'vs/base/common/arrays'; import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; @@ -47,7 +46,7 @@ export class ConfigureLocaleAction extends Action { public async run(): Promise { const languageOptions = await this.getLanguageOptions(); - const currentLanguageIndex = firstIndex(languageOptions, l => l.label === language); + const currentLanguageIndex = languageOptions.findIndex(l => l.label === language); try { const selectedLanguage = await this.quickInputService.pick(languageOptions, diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 45c6e3f2145..c1f4dd65b2f 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -346,10 +346,10 @@ class MarkerWidget extends Disposable { if (viewModel) { const quickFixAction = viewModel.quickFixAction; this.actionBar.push([quickFixAction], { icon: true, label: false }); - dom.toggleClass(this.icon, 'quickFix', quickFixAction.enabled); + this.icon.classList.toggle('quickFix', quickFixAction.enabled); quickFixAction.onDidChange(({ enabled }) => { if (!isUndefinedOrNull(enabled)) { - dom.toggleClass(this.icon, 'quickFix', enabled); + this.icon.classList.toggle('quickFix', enabled); } }, this, this.disposables); quickFixAction.onShowQuickFixes(() => { @@ -393,7 +393,7 @@ class MarkerWidget extends Disposable { } private renderDetails(marker: IMarker, filterData: MarkerFilterData | undefined, parent: HTMLElement): void { - dom.addClass(parent, 'details-container'); + parent.classList.add('details-container'); if (marker.source || marker.code) { const source = new HighlightedLabel(dom.append(parent, dom.$('.marker-source')), false); diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 528a7beeffc..2163578c6d3 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -144,7 +144,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { public renderBody(parent: HTMLElement): void { super.renderBody(parent); - dom.addClass(parent, 'markers-panel'); + parent.classList.add('markers-panel'); const container = dom.append(parent, dom.$('.markers-panel-container')); @@ -178,7 +178,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { this.smallLayout = width < 600 && height > 100; if (this.smallLayout !== wasSmallLayout) { if (this.filterActionBar) { - dom.toggleClass(this.filterActionBar.getContainer(), 'hide', !this.smallLayout); + this.filterActionBar.getContainer().classList.toggle('hide', !this.smallLayout); } } const contentHeight = this.smallLayout ? height - 44 : height; @@ -395,8 +395,8 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { private createFilterActionBar(parent: HTMLElement): void { this.filterActionBar = this._register(new ActionBar(parent, { actionViewItemProvider: action => this.getActionViewItem(action) })); - dom.addClass(this.filterActionBar.getContainer(), 'markers-panel-filter-container'); - dom.toggleClass(this.filterActionBar.getContainer(), 'hide', !this.smallLayout); + this.filterActionBar.getContainer().classList.add('markers-panel-filter-container'); + this.filterActionBar.getContainer().classList.toggle('hide', !this.smallLayout); } private createMessageBox(parent: HTMLElement): void { @@ -914,7 +914,7 @@ class MarkersTree extends WorkbenchObjectTree { } toggleVisibility(hide: boolean): void { - dom.toggleClass(this.container, 'hidden', hide); + this.container.classList.toggle('hidden', hide); } } diff --git a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts index 0f85b179dc5..5732761d20a 100644 --- a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts @@ -250,7 +250,7 @@ class FiltersDropdownMenuActionViewItem extends DropdownMenuActionViewItem { } updateChecked(): void { - DOM.toggleClass(this.element!, 'checked', this._action.checked); + this.element!.classList.toggle('checked', this._action.checked); } } @@ -285,7 +285,7 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { render(container: HTMLElement): void { this.container = container; - DOM.addClass(this.container, 'markers-panel-action-filter-container'); + this.container.classList.add('markers-panel-action-filter-container'); this.element = DOM.append(this.container, DOM.$('')); this.element.className = this.class; @@ -392,7 +392,7 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { private updateBadge(): void { if (this.filterBadge) { const { total, filtered } = this.filterController.getFilterStats(); - DOM.toggleClass(this.filterBadge, 'hidden', total === filtered || filtered === 0); + this.filterBadge.classList.toggle('hidden', total === filtered || filtered === 0); this.filterBadge.textContent = localize('showing filtered problems', "Showing {0} of {1}", filtered, total); this.adjustInputBox(); } @@ -400,7 +400,7 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { private adjustInputBox(): void { if (this.element && this.filterInputBox && this.filterBadge) { - this.filterInputBox.inputElement.style.paddingRight = DOM.hasClass(this.element, 'small') || DOM.hasClass(this.filterBadge, 'hidden') ? '25px' : '150px'; + this.filterInputBox.inputElement.style.paddingRight = this.element.classList.contains('small') || this.filterBadge.classList.contains('hidden') ? '25px' : '150px'; } } @@ -430,7 +430,7 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { protected updateClass(): void { if (this.element && this.container) { this.element.className = this.class; - DOM.toggleClass(this.container, 'grow', DOM.hasClass(this.element, 'grow')); + this.container.classList.toggle('grow', this.element.classList.contains('grow')); this.adjustInputBox(); } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 0d49ef4310c..4feefecc161 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -20,7 +20,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { BaseCellRenderTemplate, CellEditState, CellFocusMode, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED, EXPAND_CELL_CONTENT_COMMAND_ID, NOTEBOOK_CELL_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, CellUri, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, CellUri, NotebookCellMetadata, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -482,7 +482,7 @@ registerAction2(class extends NotebookCellAction { }); export function getActiveNotebookEditor(editorService: IEditorService): INotebookEditor | undefined { - // TODO can `isNotebookEditor` be on INotebookEditor to avoid a circular dependency? + // TODO@roblourens can `isNotebookEditor` be on INotebookEditor to avoid a circular dependency? const activeEditorPane = editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined; return activeEditorPane?.isNotebookEditor ? (editorService.activeEditorPane?.getControl() as INotebookEditor) : undefined; } @@ -1290,14 +1290,25 @@ registerAction2(class extends NotebookCellAction { return; } - editor.viewModel.notebookDocument.clearCellOutput(context.cell.handle); + const cell = context.cell; + const index = editor.viewModel.notebookDocument.cells.indexOf(cell.model); + + if (index < 0) { + return; + } + + editor.viewModel.notebookDocument.applyEdit(editor.viewModel.notebookDocument.versionId, [{ editType: CellEditType.Output, index, outputs: [] }], true); + if (context.cell.metadata && context.cell.metadata?.runState !== NotebookCellRunState.Running) { - context.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(context.cell.handle, { - runState: NotebookCellRunState.Idle, - runStartTime: undefined, - lastRunDuration: undefined, - statusMessage: undefined - }); + context.notebookEditor.viewModel!.notebookDocument.applyEdit(context.notebookEditor.viewModel!.notebookDocument.versionId, [{ + editType: CellEditType.Metadata, index, metadata: { + ...context.cell.metadata, + runState: NotebookCellRunState.Idle, + runStartTime: undefined, + lastRunDuration: undefined, + statusMessage: undefined + } + }], true); } } }); @@ -1423,7 +1434,10 @@ registerAction2(class extends NotebookAction { return; } - editor.viewModel.notebookDocument.clearAllCellOutputs(); + editor.viewModel.notebookDocument.applyEdit(editor.viewModel.notebookDocument.versionId, + editor.viewModel.notebookDocument.cells.map((cell, index) => ({ + editType: CellEditType.Output, index, outputs: [] + })), true); } }); @@ -1543,7 +1557,27 @@ registerAction2(class extends NotebookCellAction { } }); -registerAction2(class extends NotebookCellAction { +abstract class ChangeNotebookCellMetadataAction extends NotebookCellAction { + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + const cell = context.cell; + const textModel = context.notebookEditor.viewModel?.notebookDocument; + if (!textModel) { + return; + } + + const index = textModel.cells.indexOf(cell.model); + + if (index < 0) { + return; + } + + textModel.applyEdit(textModel.versionId, [{ editType: CellEditType.Metadata, index, metadata: { ...context.cell.metadata, ...this.getMetadataDelta() } }], true); + } + + abstract getMetadataDelta(): NotebookCellMetadata; +} + +registerAction2(class extends ChangeNotebookCellMetadataAction { constructor() { super({ id: COLLAPSE_CELL_INPUT_COMMAND_ID, @@ -1561,12 +1595,12 @@ registerAction2(class extends NotebookCellAction { }); } - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - context.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(context.cell.handle, { inputCollapsed: true }); + getMetadataDelta(): NotebookCellMetadata { + return { inputCollapsed: true }; } }); -registerAction2(class extends NotebookCellAction { +registerAction2(class extends ChangeNotebookCellMetadataAction { constructor() { super({ id: EXPAND_CELL_CONTENT_COMMAND_ID, @@ -1584,12 +1618,12 @@ registerAction2(class extends NotebookCellAction { }); } - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - context.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(context.cell.handle, { inputCollapsed: false }); + getMetadataDelta(): NotebookCellMetadata { + return { inputCollapsed: false }; } }); -registerAction2(class extends NotebookCellAction { +registerAction2(class extends ChangeNotebookCellMetadataAction { constructor() { super({ id: COLLAPSE_CELL_OUTPUT_COMMAND_ID, @@ -1607,12 +1641,12 @@ registerAction2(class extends NotebookCellAction { }); } - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - context.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(context.cell.handle, { outputCollapsed: true }); + getMetadataDelta(): NotebookCellMetadata { + return { outputCollapsed: true }; } }); -registerAction2(class extends NotebookCellAction { +registerAction2(class extends ChangeNotebookCellMetadataAction { constructor() { super({ id: EXPAND_CELL_OUTPUT_COMMAND_ID, @@ -1630,8 +1664,8 @@ registerAction2(class extends NotebookCellAction { }); } - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - context.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(context.cell.handle, { outputCollapsed: false }); + getMetadataDelta(): NotebookCellMetadata { + return { outputCollapsed: false }; } }); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts index 3b3601c8d7f..4c431ef0a48 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts @@ -43,11 +43,7 @@ export class SCMController extends Disposable implements INotebookEditorContribu this.update(); if (this._notebookEditor.textModel) { - this._localDisposable.add(this._notebookEditor.textModel.onDidChangeContent(() => { - this.update(); - })); - - this._localDisposable.add(this._notebookEditor.textModel.onDidChangeCells(() => { + this._localDisposable.add(this._notebookEditor.textModel.onDidChangeContent((e) => { this.update(); })); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 6eace8686e1..327c3ee0721 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -17,7 +17,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; -import { CellUri, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellUri, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { hash } from 'vs/base/common/hash'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -451,7 +451,15 @@ abstract class AbstractCellRenderer extends Disposable { if (newLangauge !== undefined && newLangauge !== this.cell.modified!.language) { this.notebookEditor.textModel!.changeCellLanguage(this.cell.modified!.handle, newLangauge); } - this.notebookEditor.textModel!.changeCellMetadata(this.cell.modified!.handle, result, false); + const index = this.notebookEditor.textModel!.cells.indexOf(this.cell.modified!); + + if (index < 0) { + return; + } + + this.notebookEditor.textModel!.applyEdit(this.notebookEditor.textModel!.versionId, [ + { editType: CellEditType.Metadata, index, metadata: result } + ], true); } catch { } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 5cc672c00e1..d0772635f8f 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -233,6 +233,7 @@ export interface INotebookEditor extends IEditor { getDomNode(): HTMLElement; getOverflowContainerDomNode(): HTMLElement; getInnerWebview(): Webview | undefined; + getSelectionHandles(): number[]; /** * Focus the notebook editor cell list @@ -507,7 +508,7 @@ export interface INotebookCellList { layout(height?: number, width?: number): void; dispose(): void; - // TODO resolve differences between List and INotebookCellList + // TODO@roblourens resolve differences between List and INotebookCellList getFocus(): number[]; setFocus(indexes: number[]): void; setSelection(indexes: number[]): void; @@ -698,7 +699,7 @@ export function getVisibleCells(cells: CellViewModel[], hiddenRanges: ICellRange } export function getActiveNotebookEditor(editorService: IEditorService): INotebookEditor | undefined { - // TODO can `isNotebookEditor` be on INotebookEditor to avoid a circular dependency? + // TODO@rebornix can `isNotebookEditor` be on INotebookEditor to avoid a circular dependency? const activeEditorPane = editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined; return activeEditorPane?.isNotebookEditor ? (editorService.activeEditorPane?.getControl() as INotebookEditor) : undefined; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 03aac260dd6..c3041f339cd 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -215,6 +215,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._cursorNavigationMode = v; } + private readonly _onDidChangeSelection = this._register(new Emitter()); + get onDidChangeSelection(): Event { return this._onDidChangeSelection.event; } + private readonly _onDidChangeVisibleRanges = this._register(new Emitter()); onDidChangeVisibleRanges: Event = this._onDidChangeVisibleRanges.event; @@ -268,6 +271,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return this._uuid; } + getSelectionHandles(): number[] { + return this.viewModel?.selectionHandles || []; + } + hasModel() { return !!this._notebookViewModel; } @@ -933,6 +940,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } } + this._localStore.add(this.viewModel.onDidChangeSelection(() => { + this._onDidChangeSelection.fire(); + })); + this._localStore.add(this._list!.onWillScroll(e => { this._onWillScroll.fire(e); if (!this._webviewResolved) { @@ -1778,7 +1789,7 @@ export const notebookOutputContainerColor = registerColor('notebook.outputContai hc: null }, nls.localize('notebook.outputContainerBackgroundColor', "The Color of the notebook output container background.")); -// TODO currently also used for toolbar border, if we keep all of this, pick a generic name +// TODO@rebornix currently also used for toolbar border, if we keep all of this, pick a generic name export const CELL_TOOLBAR_SEPERATOR = registerColor('notebook.cellToolbarSeparator', { dark: Color.fromHex('#808080').transparent(0.35), light: Color.fromHex('#808080').transparent(0.35), diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 3743654a065..28b08f6d08d 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -239,10 +239,12 @@ export class NotebookService extends Disposable implements INotebookService, ICu public readonly onNotebookEditorAdd: Event = this._onNotebookEditorAdd.event; private readonly _onNotebookEditorsRemove: Emitter = this._register(new Emitter()); public readonly onNotebookEditorsRemove: Event = this._onNotebookEditorsRemove.event; - private readonly _onNotebookDocumentAdd: Emitter = this._register(new Emitter()); - public readonly onNotebookDocumentAdd: Event = this._onNotebookDocumentAdd.event; - private readonly _onNotebookDocumentRemove: Emitter = this._register(new Emitter()); - public readonly onNotebookDocumentRemove: Event = this._onNotebookDocumentRemove.event; + + private readonly _onDidAddNotebookDocument = this._register(new Emitter()); + private readonly _onDidRemoveNotebookDocument = this._register(new Emitter()); + readonly onDidAddNotebookDocument = this._onDidAddNotebookDocument.event; + readonly onDidRemoveNotebookDocument = this._onDidRemoveNotebookDocument.event; + private readonly _onNotebookDocumentSaved: Emitter = this._register(new Emitter()); public readonly onNotebookDocumentSaved: Event = this._onNotebookDocumentSaved.event; private readonly _notebookEditors = new Map(); @@ -542,14 +544,13 @@ export class NotebookService extends Disposable implements INotebookService, ICu return this._notebookProviders.has(viewType); } - registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController) { + registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable { this._notebookProviders.set(viewType, { extensionData, controller }); this._onDidChangeViewTypes.fire(); - } - - unregisterNotebookProvider(viewType: string): void { - this._notebookProviders.delete(viewType); - this._onDidChangeViewTypes.fire(); + return toDisposable(() => { + this._notebookProviders.delete(viewType); + this._onDidChangeViewTypes.fire(); + }); } registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable { @@ -632,7 +633,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu ); this._models.set(uri, modelData); - this._onNotebookDocumentAdd.fire([notebookModel.uri]); + this._onDidAddNotebookDocument.fire(notebookModel); // after the document is added to the store and sent to ext host, we transform the ouputs await this.transformTextModelOutputs(notebookModel); @@ -657,7 +658,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu cell.outputs.forEach((output) => { if (output.outputKind === CellOutputKind.Rich) { - // TODO no string[] casting + // TODO@rebornix no string[] casting const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); const orderedMimeTypes = ret.orderedMimeTypes!; const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; @@ -929,8 +930,8 @@ export class NotebookService extends Disposable implements INotebookService, ICu this._onNotebookEditorsRemove.fire(willRemovedEditors.map(e => e)); - this._onNotebookDocumentRemove.fire([modelData.model.uri]); - modelData?.dispose(); + this._onDidRemoveNotebookDocument.fire(modelData.model.uri); + modelData.dispose(); } } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 0e5f4b4bda5..fcffc8ee0fd 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -48,7 +48,7 @@ import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, NotebookCellMetadata, NotebookCellRunState, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, NotebookCellMetadata, NotebookCellRunState, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparator, VerticalSeparatorViewItem } from './cellActionView'; const $ = DOM.$; @@ -332,10 +332,21 @@ abstract class AbstractCellRenderer { return; } + const textModel = this.notebookEditor.viewModel!.notebookDocument; + const index = textModel.cells.indexOf(templateData.currentRenderedCell.model); + + if (index < 0) { + return; + } + if (templateData.currentRenderedCell.metadata?.inputCollapsed) { - this.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(templateData.currentRenderedCell.handle, { inputCollapsed: false }); + textModel.applyEdit(textModel.versionId, [ + { editType: CellEditType.Metadata, index, metadata: { ...templateData.currentRenderedCell.metadata, inputCollapsed: false } } + ], true); } else if (templateData.currentRenderedCell.metadata?.outputCollapsed) { - this.notebookEditor.viewModel!.notebookDocument.deltaCellMetadata(templateData.currentRenderedCell.handle, { outputCollapsed: false }); + textModel.applyEdit(textModel.versionId, [ + { editType: CellEditType.Metadata, index, metadata: { ...templateData.currentRenderedCell.metadata, outputCollapsed: false } } + ], true); } })); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts index fbb102133c1..41bb82bb695 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts @@ -77,8 +77,8 @@ export class CellEditorStatusBar extends Disposable { return; } - this.leftContributedItemsContainer.innerHTML = ''; - this.rightContributedItemsContainer.innerHTML = ''; + DOM.clearNode(this.leftContributedItemsContainer); + DOM.clearNode(this.rightContributedItemsContainer); this.itemsDisposable.clear(); const items = this.notebookCellStatusBarService.getEntries(this.currentContext.cell.uri); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 9caa12e52b7..4355e350a66 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -88,7 +88,7 @@ function webviewPreloads() { } } - // TODO: should script with src not be removed? + // TODO@connor4312: should script with src not be removed? container.appendChild(scriptTag).parentNode!.removeChild(scriptTag); } }; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit.ts index 709c41859b2..aac728c93f2 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit.ts @@ -52,12 +52,10 @@ export class JoinCellEdit implements IResourceUndoRedoElement { const cell = this.editingDelegate.createCellViewModel(this._deletedRawCell); if (this.direction === 'above') { - this.editingDelegate.insertCell(this.index, this._deletedRawCell); - this.editingDelegate.emitSelections([cell.handle]); + this.editingDelegate.insertCell(this.index, this._deletedRawCell, [cell.handle]); cell.focusMode = CellFocusMode.Editor; } else { - this.editingDelegate.insertCell(this.index, cell.model); - this.editingDelegate.emitSelections([this.cell.handle]); + this.editingDelegate.insertCell(this.index, cell.model, [this.cell.handle]); this.cell.focusMode = CellFocusMode.Editor; } } @@ -72,8 +70,7 @@ export class JoinCellEdit implements IResourceUndoRedoElement { { range: this.inverseRange, text: this.insertContent } ]); - this.editingDelegate.deleteCell(this.index); - this.editingDelegate.emitSelections([this.cell.handle]); + this.editingDelegate.deleteCell(this.index, [this.cell.handle]); this.cell.focusMode = CellFocusMode.Editor; } } @@ -110,10 +107,9 @@ export class SplitCellEdit implements IResourceUndoRedoElement { this.cell.setSelections(this.selections); for (let j = 1; j < this.cellContents.length; j++) { - this.editingDelegate.deleteCell(this.index + 1); + this.editingDelegate.deleteCell(this.index + 1, j === this.cellContents.length - 1 ? [this.cell.handle] : undefined); } - this.editingDelegate.emitSelections([this.cell.handle]); this.cell.focusMode = CellFocusMode.Editor; } @@ -134,7 +130,6 @@ export class SplitCellEdit implements IResourceUndoRedoElement { } if (lastCell) { - this.editingDelegate.emitSelections([lastCell.handle]); lastCell.focusMode = CellFocusMode.Editor; } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index f3f812d3dda..4553fdd0412 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -23,7 +23,7 @@ import { NotebookEventDispatcher, NotebookMetadataChangedEvent } from 'vs/workbe import { CellFoldingState, EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, NotebookCellMetadata, INotebookSearchOptions, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellMetadata, INotebookSearchOptions, ICellRange, NotebookCellsChangeType, ICell, NotebookCellTextModelSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer'; @@ -214,7 +214,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD } this._selections = selections; - this._notebook.selections = selections; this._onDidChangeSelection.fire(); } @@ -248,8 +247,8 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD this.id = '$notebookViewModel' + MODEL_ID; this._instanceId = strings.singleLetterHash(MODEL_ID); - this._register(this._notebook.onDidChangeCells(e => { - const diffs = e.splices.map(splice => { + const compute = (changes: NotebookCellTextModelSplice[], synchronous: boolean) => { + const diffs = changes.map(splice => { return [splice[0], splice[1], splice[2].map(cell => { return createCellViewModel(this._instantiationService, this, cell as NotebookCellTextModel); })] as [number, number, CellViewModel[]]; @@ -272,7 +271,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD }); this._onDidChangeViewCells.fire({ - synchronous: e.synchronous, + synchronous: synchronous, splices: diffs }); @@ -303,17 +302,31 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD } this.selectionHandles = endSelectionHandles; + }; + + this._register(this._notebook.onDidChangeContent(e => { + let changes: NotebookCellTextModelSplice[] = []; + + if (e.kind === NotebookCellsChangeType.ModelChange || e.kind === NotebookCellsChangeType.Initialize) { + changes = e.changes; + compute(changes, e.synchronous); + return; + } else if (e.kind === NotebookCellsChangeType.Move) { + compute([[e.index, e.length, []]], e.synchronous); + compute([[e.newIdx, 0, e.cells]], e.synchronous); + } else { + return; + } })); - this._register(this._notebook.onDidChangeMetadata(e => { - this.eventDispatcher.emit([new NotebookMetadataChangedEvent(e)]); - })); + this._register(this._notebook.onDidChangeContent(e => { + if (e.kind === NotebookCellsChangeType.ChangeDocumentMetadata) { + this.eventDispatcher.emit([new NotebookMetadataChangedEvent(this._notebook.metadata)]); + } - this._register(this._notebook.emitSelections(selections => { - // text model emit selection change (for example, undo/redo) - // we should update the selection handle wisely - // TODO, if the editor is note selected, undo/redo should not change the focused element selection - this.updateSelectionsFromEdits(selections); + if (e.endSelections) { + this.updateSelectionsFromEdits(e.endSelections); + } })); this._register(this.eventDispatcher.onDidChangeLayout((e) => { @@ -607,13 +620,14 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD createCell(index: number, source: string, language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, synchronous: boolean, pushUndoStop: boolean = true, previouslyFocused: ICellViewModel[] = []) { const beforeSelections = previouslyFocused.map(e => e.handle); - this._notebook.createCell2(index, source, language, type, metadata, synchronous, pushUndoStop, beforeSelections, undefined); + const cell = this._notebook.createCellTextModel(source, language, type, [], metadata); + this._notebook.insertCell(index, cell, synchronous, pushUndoStop, beforeSelections, undefined); // TODO, rely on createCell to be sync return this.viewCells[index]; } insertCell(index: number, cell: NotebookCellTextModel, synchronous: boolean, pushUndoStop: boolean = true): CellViewModel { - this._notebook.insertCell2(index, cell, synchronous, pushUndoStop); + this._notebook.insertCell(index, cell, synchronous, pushUndoStop, undefined, undefined); // TODO, rely on createCell to be sync // this will trigger it to synchronous update return this._viewCells[index]; } @@ -637,7 +651,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD } } - this._notebook.deleteCell2(index, synchronous, pushUndoStop, this.selectionHandles, endSelections); + this._notebook.deleteCell(index, synchronous, pushUndoStop, this.selectionHandles, endSelections); } /** @@ -654,7 +668,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return false; } - this._notebook.moveCellToIdx2(index, length, newIdx, synchronous, pushedToUndoStack, undefined, [viewCell.handle]); + this._notebook.moveCellToIdx(index, length, newIdx, synchronous, pushedToUndoStack, undefined, [viewCell.handle]); return true; } @@ -757,9 +771,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD }, deleteCell: (index: number) => { this.deleteCell(index, true, false); - }, - emitSelections: (selections: number[]) => { - this.updateSelectionsFromEdits(selections); } } )); @@ -828,7 +839,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD insertContent, cell, { - insertCell: (index: number, cell: NotebookCellTextModel) => { + insertCell: (index: number, cell: NotebookCellTextModel, endSelections?: number[]) => { this.insertCell(index, cell, true, false); }, deleteCell: (index: number) => { @@ -836,9 +847,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD }, createCellViewModel: (cell: NotebookCellTextModel) => { return createCellViewModel(this._instantiationService, this, cell); - }, - emitSelections: (selections: number[]) => { - this.updateSelectionsFromEdits(selections); } }) ); @@ -882,7 +890,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD insertContent, below, { - insertCell: (index: number, cell: NotebookCellTextModel) => { + insertCell: (index: number, cell: NotebookCellTextModel, endSelections?: number[]) => { this.insertCell(index, cell, true, false); }, deleteCell: (index: number) => { @@ -890,9 +898,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD }, createCellViewModel: (cell: NotebookCellTextModel) => { return createCellViewModel(this._instantiationService, this, cell); - }, - emitSelections: (selections: number[]) => { - this.updateSelectionsFromEdits(selections); } }) ); diff --git a/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts b/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts index 5cde6b3bfaa..9a9c8c2a3d6 100644 --- a/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts +++ b/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts @@ -12,11 +12,10 @@ import { NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/noteb * It should not modify Undo/Redo stack */ export interface ITextCellEditingDelegate { - insertCell?(index: number, cell: NotebookCellTextModel): void; - deleteCell?(index: number): void; + insertCell?(index: number, cell: NotebookCellTextModel, endSelections?: number[]): void; + deleteCell?(index: number, endSelections?: number[]): void; moveCell?(fromIndex: number, length: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined): void; updateCellMetadata?(index: number, newMetadata: NotebookCellMetadata): void; - emitSelections(selections: number[]): void; } @@ -38,20 +37,14 @@ export class InsertCellEdit implements IResourceUndoRedoElement { throw new Error('Notebook Delete Cell not implemented for Undo/Redo'); } - this.editingDelegate.deleteCell(this.insertIndex); - if (this.beforedSelections) { - this.editingDelegate.emitSelections(this.beforedSelections); - } + this.editingDelegate.deleteCell(this.insertIndex, this.beforedSelections); } redo(): void { if (!this.editingDelegate.insertCell) { throw new Error('Notebook Insert Cell not implemented for Undo/Redo'); } - this.editingDelegate.insertCell(this.insertIndex, this.cell); - if (this.endSelections) { - this.editingDelegate.emitSelections(this.endSelections); - } + this.editingDelegate.insertCell(this.insertIndex, this.cell, this.endSelections); } } @@ -77,10 +70,7 @@ export class DeleteCellEdit implements IResourceUndoRedoElement { throw new Error('Notebook Insert Cell not implemented for Undo/Redo'); } - this.editingDelegate.insertCell(this.insertIndex, this._cell); - if (this.beforedSelections) { - this.editingDelegate.emitSelections(this.beforedSelections); - } + this.editingDelegate.insertCell(this.insertIndex, this._cell, this.beforedSelections); } redo(): void { @@ -88,10 +78,7 @@ export class DeleteCellEdit implements IResourceUndoRedoElement { throw new Error('Notebook Delete Cell not implemented for Undo/Redo'); } - this.editingDelegate.deleteCell(this.insertIndex); - if (this.endSelections) { - this.editingDelegate.emitSelections(this.endSelections); - } + this.editingDelegate.deleteCell(this.insertIndex, this.endSelections); } } @@ -116,9 +103,6 @@ export class MoveCellEdit implements IResourceUndoRedoElement { } this.editingDelegate.moveCell(this.toIndex, this.length, this.fromIndex, this.endSelections, this.beforedSelections); - if (this.beforedSelections) { - this.editingDelegate.emitSelections(this.beforedSelections); - } } redo(): void { @@ -127,9 +111,6 @@ export class MoveCellEdit implements IResourceUndoRedoElement { } this.editingDelegate.moveCell(this.fromIndex, this.length, this.toIndex, this.beforedSelections, this.endSelections); - if (this.endSelections) { - this.editingDelegate.emitSelections(this.endSelections); - } } } @@ -152,17 +133,13 @@ export class SpliceCellsEdit implements IResourceUndoRedoElement { this.diffs.forEach(diff => { for (let i = 0; i < diff[2].length; i++) { - this.editingDelegate.deleteCell!(diff[0]); + this.editingDelegate.deleteCell!(diff[0], this.beforeHandles); } diff[1].reverse().forEach(cell => { - this.editingDelegate.insertCell!(diff[0], cell); + this.editingDelegate.insertCell!(diff[0], cell, this.beforeHandles); }); }); - - if (this.beforeHandles) { - this.editingDelegate.emitSelections(this.beforeHandles); - } } redo(): void { @@ -172,17 +149,13 @@ export class SpliceCellsEdit implements IResourceUndoRedoElement { this.diffs.reverse().forEach(diff => { for (let i = 0; i < diff[1].length; i++) { - this.editingDelegate.deleteCell!(diff[0]); + this.editingDelegate.deleteCell!(diff[0], this.endHandles); } diff[2].reverse().forEach(cell => { - this.editingDelegate.insertCell!(diff[0], cell); + this.editingDelegate.insertCell!(diff[0], cell, this.endHandles); }); }); - - if (this.endHandles) { - this.editingDelegate.emitSelections(this.endHandles); - } } } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index c2b34adefcf..aa822ae32cd 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -108,7 +108,7 @@ export class NotebookCellTextModel extends Disposable implements ICell { return this._hash; } - // TODO, raw outputs + // TODO@rebornix, raw outputs this._hash = hash([hash(this.getValue()), this._getPersisentMetadata, this.transientOptions.transientOutputs ? [] : this._outputs]); return this._hash; } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index e30e36efe6f..5537f5d9f15 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -5,10 +5,10 @@ import * as nls from 'vs/nls'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, NotebookCellsChangeType, ICellDto2, IMainCellDto, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookTextModel, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, NotebookCellsChangeType, ICellDto2, TransientOptions, NotebookTextModelChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITextSnapshot } from 'vs/editor/common/model'; import { IUndoRedoService, UndoRedoElementType, IUndoRedoElement, IResourceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; import { InsertCellEdit, DeleteCellEdit, MoveCellEdit, SpliceCellsEdit, CellMetadataEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit'; @@ -101,27 +101,52 @@ export class NotebookOperationManager { } } +class DelayedEmitter { + constructor( + private readonly _onDidChangeContent: Emitter, + private readonly _increaseVersion: () => void, + private readonly _textModel: NotebookTextModel + + ) { + + } + + emit(data: { + triggerDirty: { value: boolean } | undefined, + modelContentChange: { value: NotebookTextModelChangedEvent } | undefined, + }) { + this._increaseVersion(); + + if (data.triggerDirty) { + this._textModel.setDirty(data.triggerDirty.value); + } + + if (data.modelContentChange) { + this._onDidChangeContent.fire( + { + ...data.modelContentChange.value, + versionId: this._textModel.versionId + } + ); + } + } +} + export class NotebookTextModel extends Disposable implements INotebookTextModel { - private _cellhandlePool: number = 0; - private readonly _onWillDispose: Emitter = this._register(new Emitter()); + private readonly _onDidChangeContent = this._register(new Emitter()); + private readonly _onDidChangeMetadata = this._register(new Emitter()); + readonly onWillDispose: Event = this._onWillDispose.event; - private readonly _onDidChangeCells = this._register(new Emitter<{ synchronous: boolean, splices: NotebookCellTextModelSplice[] }>()); - get onDidChangeCells() { return this._onDidChangeCells.event; } - private readonly _emitSelections = this._register(new Emitter()); - get emitSelections() { return this._emitSelections.event; } - private _onDidModelChangeProxy = this._register(new Emitter()); - get onDidModelChangeProxy(): Event { return this._onDidModelChangeProxy.event; } - private _onDidSelectionChangeProxy = this._register(new Emitter()); - get onDidSelectionChange(): Event { return this._onDidSelectionChangeProxy.event; } - private _onDidChangeContent = this._register(new Emitter()); - onDidChangeContent: Event = this._onDidChangeContent.event; - private _onDidChangeMetadata = this._register(new Emitter()); - onDidChangeMetadata: Event = this._onDidChangeMetadata.event; + readonly onDidChangeContent = this._onDidChangeContent.event; + readonly onDidChangeMetadata = this._onDidChangeMetadata.event; + + private _cellhandlePool: number = 0; private _mapping: Map = new Map(); private _cellListeners: Map = new Map(); - cells: NotebookCellTextModel[]; + private _cells: NotebookCellTextModel[]; + private _languages: string[] = []; private _allLanguages: boolean = false; @@ -142,26 +167,22 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel private _isUntitled: boolean | undefined = undefined; private _versionId = 0; - get versionId() { - return this._versionId; - } + //#region selection TODO@rebornix this shouldn't be here - private _selections: number[] = []; + private readonly _emitSelections = this._register(new Emitter()); + readonly emitSelections = this._emitSelections.event; + private _onDidSelectionChangeProxy = this._register(new Emitter()); + readonly onDidSelectionChange = this._onDidSelectionChangeProxy.event; - get selections() { - return this._selections; - } - set selections(selections: number[]) { - this._selections = selections; - this._onDidSelectionChangeProxy.fire(this._selections); - } + private _operationManager: NotebookOperationManager; + private _eventEmitter: DelayedEmitter; private _dirty = false; protected readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; - private _operationManager: NotebookOperationManager; + constructor( readonly viewType: string, @@ -172,9 +193,29 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel @IModeService private readonly _modeService: IModeService, ) { super(); - this.cells = []; + this._cells = []; this._operationManager = new NotebookOperationManager(this._undoService, uri); + this._eventEmitter = new DelayedEmitter( + this._onDidChangeContent, + () => { this._increaseVersionId(); }, + this + ); + } + + dispose() { + this._onWillDispose.fire(); + dispose(this._cellListeners.values()); + dispose(this._cells); + super.dispose(); + } + + get cells(): readonly NotebookCellTextModel[] { + return this._cells; + } + + get versionId() { + return this._versionId; } get isDirty() { @@ -201,7 +242,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } initialize(cells: ICellDto2[]) { - this.cells = []; + this._cells = []; this._versionId = 0; const mainCells = cells.map(cell => { @@ -217,13 +258,13 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const dirtyStateListener = mainCells[i].onDidChangeContent(() => { this.setDirty(true); this._increaseVersionId(); - this._onDidChangeContent.fire(); + this._onDidChangeContent.fire({ kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true }); }); this._cellListeners.set(mainCells[i].handle, dirtyStateListener); } - this.cells.splice(0, 0, ...mainCells); + this._cells.splice(0, 0, ...mainCells); this._increaseVersionId(); } @@ -231,14 +272,21 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._operationManager.pushStackElement(label); } + pushEditOperations(modelVersionId: number, rawEdits: ICellEditOperation[], synchronous: boolean) { + // this._eventEmitter.beginDeferredEmit(); + this.pushStackElement('edit'); + // every edit should push a pending element into `edit` undo stack element. + this.applyEdit(modelVersionId, rawEdits, synchronous); + this.pushStackElement('edit'); + // this._eventEmitter.endDeferredEmit(); + + } + applyEdit(modelVersionId: number, rawEdits: ICellEditOperation[], synchronous: boolean): boolean { if (modelVersionId !== this._versionId) { return false; } - const oldViewCells = this.cells.slice(0); - const oldMap = new Map(this._mapping); - const edits = rawEdits.map((edit, index) => { return { edit, @@ -252,69 +300,38 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel for (const { edit } of edits) { switch (edit.editType) { case CellEditType.Replace: - this._replaceCells(edit.index, edit.count, edit.cells); + this._replaceCells(edit.index, edit.count, edit.cells, synchronous); break; case CellEditType.Output: //TODO@joh,@rebornix no event, no undo stop (?) - this.assertIndex(edit.index); - const cell = this.cells[edit.index]; - this.spliceNotebookCellOutputs(cell.handle, [[0, cell.outputs.length, edit.outputs]]); + this._assertIndex(edit.index); + const cell = this._cells[edit.index]; + // TODO@rebornix, we should do diff first + this._spliceNotebookCellOutputs(cell.handle, [[0, cell.outputs.length, edit.outputs]]); break; case CellEditType.Metadata: - this.assertIndex(edit.index); - this.deltaCellMetadata(this.cells[edit.index].handle, edit.metadata); + this._assertIndex(edit.index); + this._changeCellMetadata(this._cells[edit.index].handle, edit.metadata, true); break; } } - const diffs = diff(oldViewCells, this.cells, cell => { - return oldMap.has(cell.handle); - }).map(diff => { - return [diff.start, diff.deleteCount, diff.toInsert] as [number, number, NotebookCellTextModel[]]; - }); - - this._onDidModelChangeProxy.fire({ - kind: NotebookCellsChangeType.ModelChange, - versionId: this._versionId, - changes: diffs.map(diff => [diff[0], diff[1], diff[2].map(cell => ({ - handle: cell.handle, - uri: cell.uri, - source: cell.textBuffer.getLinesContent(), - eol: cell.textBuffer.getEOL(), - language: cell.language, - cellKind: cell.cellKind, - outputs: cell.outputs, - metadata: cell.metadata - }))] as [number, number, IMainCellDto[]]) - }); - - const undoDiff = diffs.map(diff => { - const deletedCells = this.cells.slice(diff[0], diff[0] + diff[1]); - - return [diff[0], deletedCells, diff[2]] as [number, NotebookCellTextModel[], NotebookCellTextModel[]]; - }); - - this._operationManager.pushEditOperation(new SpliceCellsEdit(this.uri, undoDiff, { - insertCell: this._insertCellDelegate.bind(this), - deleteCell: this._deleteCellDelegate.bind(this), - emitSelections: this._emitSelectionsDelegate.bind(this) - }, undefined, undefined)); - - this._onDidChangeCells.fire({ synchronous: synchronous, splices: diffs }); return true; } - private _replaceCells(index: number, count: number, cellDtos: ICellDto2[]): void { + private _replaceCells(index: number, count: number, cellDtos: ICellDto2[], synchronous: boolean): void { if (count === 0 && cellDtos.length === 0) { return; } this._isUntitled = false; //TODO@rebornix fishy? + const oldViewCells = this._cells.slice(0); + const oldMap = new Map(this._mapping); // prepare remove for (let i = index; i < index + count; i++) { - const cell = this.cells[i]; + const cell = this._cells[i]; this._cellListeners.get(cell.handle)?.dispose(); this._cellListeners.delete(cell.handle); } @@ -329,9 +346,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._modelService ); const dirtyStateListener = cell.onDidChangeContent(() => { - this.setDirty(true); - this._increaseVersionId(); - this._onDidChangeContent.fire(); + this._eventEmitter.emit({ + triggerDirty: { value: true }, + modelContentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true } } + }); }); this._cellListeners.set(cell.handle, dirtyStateListener); this._mapping.set(cell.handle, cell); @@ -339,10 +357,36 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel }); // make change - this.cells.splice(index, count, ...cells); - this.setDirty(true); - this._increaseVersionId(); - this._onDidChangeContent.fire(); + this._cells.splice(index, count, ...cells); + const diffs = diff(oldViewCells, this._cells, cell => { + return oldMap.has(cell.handle); + }).map(diff => { + return [diff.start, diff.deleteCount, diff.toInsert] as [number, number, NotebookCellTextModel[]]; + }); + + const undoDiff = diffs.map(diff => { + const deletedCells = this._cells.slice(diff[0], diff[0] + diff[1]); + + return [diff[0], deletedCells, diff[2]] as [number, NotebookCellTextModel[], NotebookCellTextModel[]]; + }); + + this._operationManager.pushEditOperation(new SpliceCellsEdit(this.uri, undoDiff, { + insertCell: (index, cell, endSelections?: number[]) => { this._insertNewCell(index, [cell], true, endSelections); }, + deleteCell: (index, endSelections?: number[]) => { this._removeCell(index, 1, true, endSelections); }, + }, undefined, undefined)); + + // should be deferred + this._eventEmitter.emit({ + triggerDirty: { value: true }, + modelContentChange: { + value: { + kind: NotebookCellsChangeType.ModelChange, + versionId: this._versionId, + changes: diffs, + synchronous + } + } + }); } handleEdit(label: string | undefined, undo: () => void, redo: () => void): void { @@ -378,185 +422,119 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._languages = languages; const resolvedLanguages = this.resolvedLanguages; - if (this._isUntitled && resolvedLanguages.length && this.cells.length) { - this.cells[0].language = resolvedLanguages[0]; + if (this._isUntitled && resolvedLanguages.length && this._cells.length) { + this._cells[0].language = resolvedLanguages[0]; } } updateNotebookMetadata(metadata: NotebookDocumentMetadata) { this.metadata = metadata; - this._onDidChangeMetadata.fire(this.metadata); + this._eventEmitter.emit({ + triggerDirty: { value: true }, + modelContentChange: { value: { kind: NotebookCellsChangeType.ChangeDocumentMetadata, versionId: this.versionId, metadata: this.metadata, synchronous: true } } + }); } insertTemplateCell(cell: NotebookCellTextModel) { - if (this.cells.length > 0 || this._isUntitled !== undefined) { + if (this._cells.length > 0 || this._isUntitled !== undefined) { return; } this._isUntitled = true; - this.cells = [cell]; + this._cells = [cell]; this._mapping.set(cell.handle, cell); const dirtyStateListener = cell.onDidChangeContent(() => { this._isUntitled = false; - this.setDirty(true); - this._increaseVersionId(); - this._onDidChangeContent.fire(); + this._eventEmitter.emit({ + triggerDirty: { value: true }, + modelContentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true } } + }); }); this._cellListeners.set(cell.handle, dirtyStateListener); this.setDirty(false); - this._onDidChangeContent.fire(); - this._onDidModelChangeProxy.fire({ + // this._eventEmitter.emit({ + // triggerDirty: undefined, + // modelContentChange: { value: { + // kind: NotebookCellsChangeType.ModelChange, + // versionId: this._versionId, changes: + // [[ + // 0, + // 0, + // [cell] + // ]], + // synchronous: true + // } } + // }); + + this.setDirty(false); + // this._onDidChangeContent.fire(NotebookCellsChangeType.ModelChange); + + this._onDidChangeContent.fire({ kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: [[ 0, 0, - [{ - handle: cell.handle, - uri: cell.uri, - source: cell.textBuffer.getLinesContent(), - eol: cell.textBuffer.getEOL(), - language: cell.language, - cellKind: cell.cellKind, - outputs: cell.outputs, - metadata: cell.metadata - }] - ]] + [cell] + ]], + synchronous: true }); return; } - insertNewCell(index: number, cells: NotebookCellTextModel[], emitToExtHost: boolean = true): void { + private _insertNewCell(index: number, cells: NotebookCellTextModel[], synchronous: boolean, endSelections?: number[]): void { this._isUntitled = false; for (let i = 0; i < cells.length; i++) { this._mapping.set(cells[i].handle, cells[i]); const dirtyStateListener = cells[i].onDidChangeContent(() => { - this.setDirty(true); - this._increaseVersionId(); - this._onDidChangeContent.fire(); + this._eventEmitter.emit({ + triggerDirty: { value: true }, + modelContentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true } } + }); }); this._cellListeners.set(cells[i].handle, dirtyStateListener); } - this.cells.splice(index, 0, ...cells); - this.setDirty(true); - this._onDidChangeContent.fire(); - - this._increaseVersionId(); - - if (emitToExtHost) { - this._onDidModelChangeProxy.fire({ - kind: NotebookCellsChangeType.ModelChange, - versionId: this._versionId, changes: - [[ - index, - 0, - cells.map(cell => ({ - handle: cell.handle, - uri: cell.uri, - source: cell.textBuffer.getLinesContent(), - eol: cell.textBuffer.getEOL(), - language: cell.language, - cellKind: cell.cellKind, - outputs: cell.outputs, - metadata: cell.metadata - })) - ]] - }); - } + this._cells.splice(index, 0, ...cells); + this._eventEmitter.emit({ + triggerDirty: { value: true }, + modelContentChange: { + value: { + kind: NotebookCellsChangeType.ModelChange, + versionId: this._versionId, changes: + [[ + index, + 0, + cells + ]], + synchronous, + endSelections: endSelections + } + } + }); return; } - removeCell(index: number, count: number, emitToExtHost: boolean = true) { + private _removeCell(index: number, count: number, synchronous: boolean, endSelections?: number[]) { this._isUntitled = false; for (let i = index; i < index + count; i++) { - const cell = this.cells[i]; + const cell = this._cells[i]; this._cellListeners.get(cell.handle)?.dispose(); this._cellListeners.delete(cell.handle); } - this.cells.splice(index, count); - this.setDirty(true); - this._onDidChangeContent.fire(); - - this._increaseVersionId(); - if (emitToExtHost) { - this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: [[index, count, []]] }); - } - } - - moveCellToIdx(index: number, length: number, newIdx: number, emitToExtHost: boolean = true) { - this.assertIndex(index); - this.assertIndex(newIdx); - - const cells = this.cells.splice(index, length); - this.cells.splice(newIdx, 0, ...cells); - this.setDirty(true); - this._onDidChangeContent.fire(); - - this._increaseVersionId(); - - if (emitToExtHost) { - this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.Move, versionId: this._versionId, index, newIdx }); - } - } - - assertIndex(index: number) { - if (index < 0 || index >= this.cells.length) { - throw new Error(`model index out of range ${index}`); - } - } - - // TODO@rebornix should this trigger content change event? - spliceNotebookCellOutputs(cellHandle: number, splices: NotebookCellOutputsSplice[]): void { - const cell = this._mapping.get(cellHandle); - if (cell) { - - cell.spliceNotebookCellOutputs(splices); - - if (!this.transientOptions.transientOutputs) { - this._increaseVersionId(); - this.setDirty(true); - this._onDidChangeContent.fire(); - } - - this._onDidModelChangeProxy.fire({ - kind: NotebookCellsChangeType.Output, - versionId: this.versionId, - index: this.cells.indexOf(cell), - outputs: cell.outputs ?? [] - }); - } - - } - - clearCellOutput(handle: number) { - const cell = this._mapping.get(handle); - if (cell) { - cell.spliceNotebookCellOutputs([ - [0, cell.outputs.length, []] - ]); - - this._increaseVersionId(); - this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.CellClearOutput, versionId: this._versionId, index: this.cells.indexOf(cell) }); - } - } - - changeCellLanguage(handle: number, languageId: string) { - const cell = this._mapping.get(handle); - if (cell && cell.language !== languageId) { - cell.language = languageId; - - this._increaseVersionId(); - this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.ChangeLanguage, versionId: this._versionId, index: this.cells.indexOf(cell), language: languageId }); - } + this._cells.splice(index, count); + this._eventEmitter.emit({ + triggerDirty: { value: true }, + modelContentChange: { value: { kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: [[index, count, []]], synchronous, endSelections } } + }); } private _isCellMetadataChanged(a: NotebookCellMetadata, b: NotebookCellMetadata) { @@ -574,8 +552,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel return false; } - changeCellMetadata(handle: number, metadata: NotebookCellMetadata, pushUndoStop: boolean) { - const cell = this.cells.find(cell => cell.handle === handle); + private _changeCellMetadata(handle: number, metadata: NotebookCellMetadata, pushUndoStop: boolean) { + const cell = this._cells.find(cell => cell.handle === handle); if (!cell) { return; @@ -585,142 +563,123 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel if (triggerDirtyChange) { if (pushUndoStop) { - const index = this.cells.indexOf(cell); + const index = this._cells.indexOf(cell); this._operationManager.pushEditOperation(new CellMetadataEdit(this.uri, index, Object.freeze(cell.metadata), Object.freeze(metadata), { updateCellMetadata: (index, newMetadata) => { - const cell = this.cells[index]; + const cell = this._cells[index]; if (!cell) { return; } - this.changeCellMetadata(cell.handle, newMetadata, false); - }, - emitSelections: this._emitSelectionsDelegate.bind(this) + this._changeCellMetadata(cell.handle, newMetadata, false); + } })); } + // should be deferred cell.metadata = metadata; - this.setDirty(true); - this._onDidChangeContent.fire(); } else { cell.metadata = metadata; } - this._increaseVersionId(); - this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.ChangeMetadata, versionId: this._versionId, index: this.cells.indexOf(cell), metadata: cell.metadata }); - } - - deltaCellMetadata(handle: number, newMetadata: NotebookCellMetadata) { - const cell = this._mapping.get(handle); - if (cell) { - this.changeCellMetadata(handle, { - ...cell.metadata, - ...newMetadata - }, true); - } - } - - clearAllCellOutputs() { - this.cells.forEach(cell => { - cell.spliceNotebookCellOutputs([ - [0, cell.outputs.length, []] - ]); + this._eventEmitter.emit({ + triggerDirty: { value: triggerDirtyChange }, + modelContentChange: { + value: { kind: NotebookCellsChangeType.ChangeCellMetadata, versionId: this._versionId, index: this._cells.indexOf(cell), metadata: cell.metadata, synchronous: true } + } }); - this._increaseVersionId(); - this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.CellsClearOutput, versionId: this._versionId }); + } + + // TODO@rebornix, once adopted the new Edit API in ext host, the method should be private. + _spliceNotebookCellOutputs(cellHandle: number, splices: NotebookCellOutputsSplice[]): void { + const cell = this._mapping.get(cellHandle); + if (cell) { + cell.spliceNotebookCellOutputs(splices); + + this._eventEmitter.emit({ + triggerDirty: { value: !this.transientOptions.transientOutputs }, + modelContentChange: { + value: { + kind: NotebookCellsChangeType.Output, + versionId: this.versionId, + index: this._cells.indexOf(cell), + outputs: cell.outputs ?? [], + transient: this.transientOptions.transientOutputs, + synchronous: true + } + } + }); + } + + } + + private _assertIndex(index: number) { + if (index < 0 || index >= this._cells.length) { + throw new Error(`model index out of range ${index}`); + } } //#region Notebook Text Model Edit API - private _insertCellDelegate(insertIndex: number, insertCell: NotebookCellTextModel) { - this.insertNewCell(insertIndex, [insertCell]); - this._onDidChangeCells.fire({ synchronous: true, splices: [[insertIndex, 0, [insertCell]]] }); + changeCellLanguage(handle: number, languageId: string) { + const cell = this._mapping.get(handle); + if (cell && cell.language !== languageId) { + cell.language = languageId; + + this._eventEmitter.emit({ + triggerDirty: undefined, + modelContentChange: { value: { kind: NotebookCellsChangeType.ChangeLanguage, versionId: this._versionId, index: this._cells.indexOf(cell), language: languageId, synchronous: true } } + }); + } } - private _deleteCellDelegate(deleteIndex: number) { - this.removeCell(deleteIndex, 1); - this._onDidChangeCells.fire({ synchronous: true, splices: [[deleteIndex, 1, []]] }); - } - - private _emitSelectionsDelegate(selections: number[]) { - this._emitSelections.fire(selections); - } - - createCell2(index: number, source: string, language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, synchronous: boolean, pushUndoStop: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined) { - const cell = this.createCellTextModel(source, language, type, [], metadata); - + insertCell(index: number, cell: NotebookCellTextModel, synchronous: boolean, pushUndoStop: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined): void { if (pushUndoStop) { this._operationManager.pushEditOperation(new InsertCellEdit(this.uri, index, cell, { - insertCell: this._insertCellDelegate.bind(this), - deleteCell: this._deleteCellDelegate.bind(this), - emitSelections: this._emitSelectionsDelegate.bind(this) + insertCell: (index, cell, endSelections) => { this._insertNewCell(index, [cell], true, endSelections); }, + deleteCell: (index, endSelections) => { this._removeCell(index, 1, true, endSelections); }, }, beforeSelections, endSelections)); } - - this.insertNewCell(index, [cell]); - - this._onDidChangeCells.fire({ synchronous, splices: [[index, 0, [cell]]] }); - - if (endSelections) { - this._emitSelections.fire(endSelections); - } - return cell; + this._insertNewCell(index, [cell], synchronous, endSelections); } - insertCell2(index: number, cell: NotebookCellTextModel, synchronous: boolean, pushUndoStop: boolean): void { - if (pushUndoStop) { - this._operationManager.pushEditOperation(new InsertCellEdit(this.uri, index, cell, { - insertCell: this._insertCellDelegate.bind(this), - deleteCell: this._deleteCellDelegate.bind(this), - emitSelections: this._emitSelectionsDelegate.bind(this) - }, undefined, undefined)); - } - - this.insertNewCell(index, [cell]); - this._onDidChangeCells.fire({ synchronous: synchronous, splices: [[index, 0, [cell]]] }); - } - - deleteCell2(index: number, synchronous: boolean, pushUndoStop: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined) { - const cell = this.cells[index]; + deleteCell(index: number, synchronous: boolean, pushUndoStop: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined) { + const cell = this._cells[index]; if (pushUndoStop) { this._operationManager.pushEditOperation(new DeleteCellEdit(this.uri, index, cell, { - insertCell: this._insertCellDelegate.bind(this), - deleteCell: this._deleteCellDelegate.bind(this), - emitSelections: this._emitSelectionsDelegate.bind(this) + insertCell: (index, cell, endSelections) => { this._insertNewCell(index, [cell], true, endSelections); }, + deleteCell: (index, endSelections) => { this._removeCell(index, 1, true, endSelections); }, }, beforeSelections, endSelections)); } - this.removeCell(index, 1); - this._onDidChangeCells.fire({ synchronous: synchronous, splices: [[index, 1, []]] }); - if (endSelections) { - this._emitSelections.fire(endSelections); - } + this._removeCell(index, 1, synchronous, endSelections); } - moveCellToIdx2(index: number, length: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined): boolean { - const cells = this.cells.slice(index, index + length); + moveCellToIdx(index: number, length: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined): boolean { if (pushedToUndoStack) { this._operationManager.pushEditOperation(new MoveCellEdit(this.uri, index, length, newIdx, { moveCell: (fromIndex: number, length: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined) => { - this.moveCellToIdx2(fromIndex, length, toIndex, true, false, beforeSelections, endSelections); + this.moveCellToIdx(fromIndex, length, toIndex, true, false, beforeSelections, endSelections); }, - emitSelections: this._emitSelectionsDelegate.bind(this) }, beforeSelections, endSelections)); } - this.moveCellToIdx(index, length, newIdx); - // todo, we can't emit this change as it will create a new view model and that will hold - // a new reference to the document, thus - this._onDidChangeCells.fire({ synchronous: synchronous, splices: [[index, length, []]] }); - this._onDidChangeCells.fire({ synchronous: synchronous, splices: [[newIdx, 0, cells]] }); - if (endSelections) { - this._emitSelections.fire(endSelections); + this._assertIndex(index); + this._assertIndex(newIdx); + + { + const cells = this._cells.splice(index, length); + this._cells.splice(newIdx, 0, ...cells); + this._eventEmitter.emit({ + triggerDirty: { value: true }, + modelContentChange: { value: { kind: NotebookCellsChangeType.Move, versionId: this._versionId, index, length, newIdx, cells, synchronous, endSelections } } + }); } return true; } async splitNotebookCell(index: number, newLinesContents: string[], endSelections: number[]) { - const cell = this.cells[index]; + const cell = this._cells[index]; const ref = await cell.resolveTextModelRef(); const textModel = ref.object.textEditorModel; @@ -737,19 +696,12 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel let insertIndex = index + 1; const newCells = []; for (let j = 1; j < newLinesContents.length; j++, insertIndex++) { - newCells.push(this.createCell2(insertIndex, newLinesContents[j], language, kind, undefined, true, false, undefined, undefined)); - } - - if (endSelections) { - this._emitSelections.fire(endSelections); + const cell = this.createCellTextModel(newLinesContents[j], language, kind, [], undefined); + this.insertCell(insertIndex, cell, true, false, undefined, j === newLinesContents.length - 1 ? endSelections : undefined); + newCells.push(cell); } } //#endregion - dispose() { - this._onWillDispose.fire(); - this._cellListeners.forEach(val => val.dispose()); - this.cells.forEach(cell => cell.dispose()); - super.dispose(); - } + } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index fe94a91d7ce..75b8abd0a88 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -270,14 +270,12 @@ export interface IMetadata { } export interface INotebookTextModel { - viewType: string; + readonly viewType: string; metadata: NotebookDocumentMetadata readonly uri: URI; readonly versionId: number; languages: string[]; - cells: ICell[]; - onDidChangeCells?: Event<{ synchronous: boolean, splices: NotebookCellTextModelSplice[] }>; - onDidChangeContent: Event; + readonly cells: readonly ICell[]; onWillDispose(listener: () => void): IDisposable; } @@ -311,10 +309,10 @@ export type IRenderOutput = IRenderNoOutput | IInsetRenderOutput; export const outputHasDynamicHeight = (o: IRenderOutput) => o.type !== RenderOutputType.Extension && o.hasDynamicHeight; -export type NotebookCellTextModelSplice = [ +export type NotebookCellTextModelSplice = [ number /* start */, number, - ICell[] + T[] ]; export type NotebookCellOutputsSplice = [ @@ -347,26 +345,35 @@ export enum NotebookCellsChangeType { CellsClearOutput = 4, ChangeLanguage = 5, Initialize = 6, - ChangeMetadata = 7, + ChangeCellMetadata = 7, Output = 8, + ChangeCellContent = 9, + ChangeDocumentMetadata = 10 } -export interface NotebookCellsInitializeEvent { +export interface NotebookCellsInitializeEvent { readonly kind: NotebookCellsChangeType.Initialize; - readonly changes: NotebookCellsSplice2[]; + readonly changes: NotebookCellTextModelSplice[]; readonly versionId: number; } -export interface NotebookCellsModelChangedEvent { +export interface NotebookCellContentChangeEvent { + readonly kind: NotebookCellsChangeType.ChangeCellContent; + readonly versionId: number; +} + +export interface NotebookCellsModelChangedEvent { readonly kind: NotebookCellsChangeType.ModelChange; - readonly changes: NotebookCellsSplice2[]; + readonly changes: NotebookCellTextModelSplice[]; readonly versionId: number; } -export interface NotebookCellsModelMoveEvent { +export interface NotebookCellsModelMoveEvent { readonly kind: NotebookCellsChangeType.Move; readonly index: number; + readonly length: number; readonly newIdx: number; + readonly cells: T[]; readonly versionId: number; } @@ -375,17 +382,7 @@ export interface NotebookOutputChangedEvent { readonly index: number; readonly versionId: number; readonly outputs: IProcessedOutput[]; -} - -export interface NotebookCellClearOutputEvent { - readonly kind: NotebookCellsChangeType.CellClearOutput; - readonly index: number; - readonly versionId: number; -} - -export interface NotebookCellsClearOutputEvent { - readonly kind: NotebookCellsChangeType.CellsClearOutput; - readonly versionId: number; + readonly transient: boolean; } export interface NotebookCellsChangeLanguageEvent { @@ -396,13 +393,20 @@ export interface NotebookCellsChangeLanguageEvent { } export interface NotebookCellsChangeMetadataEvent { - readonly kind: NotebookCellsChangeType.ChangeMetadata; + readonly kind: NotebookCellsChangeType.ChangeCellMetadata; readonly versionId: number; readonly index: number; readonly metadata: NotebookCellMetadata | undefined; } -export type NotebookCellsChangedEvent = NotebookCellsInitializeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookCellClearOutputEvent | NotebookCellsClearOutputEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent; +export interface NotebookDocumentChangeMetadataEvent { + readonly kind: NotebookCellsChangeType.ChangeDocumentMetadata; + readonly versionId: number; + readonly metadata: NotebookDocumentMetadata | undefined; +} + +export type NotebookCellsChangedEventDto = NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent; +export type NotebookTextModelChangedEvent = (NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent) & { synchronous: boolean; endSelections?: number[] }; export const enum CellEditType { Replace = 1, @@ -687,6 +691,8 @@ export interface IEditor extends editorCommon.ICompositeCodeEditor { readonly onDidChangeModel: Event; readonly onDidFocusEditorWidget: Event; readonly onDidChangeVisibleRanges: Event; + readonly onDidChangeSelection: Event; + getSelectionHandles(): number[]; isNotebookEditor: boolean; visibleRanges: ICellRange[]; uri?: URI; diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 9e3a0c944a4..bc6ee9afeb8 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { EditorModel, IRevertOptions } from 'vs/workbench/common/editor'; import { Emitter, Event } from 'vs/base/common/event'; -import { INotebookEditorModel, NotebookDocumentBackupData } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookEditorModel, NotebookCellsChangeType, NotebookDocumentBackupData } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { URI } from 'vs/base/common/uri'; @@ -145,9 +145,12 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM this._register(this._notebook); - this._register(this._notebook.onDidChangeContent(() => { - this._onDidChangeContent.fire(); + this._register(this._notebook.onDidChangeContent(e => { + if (e.kind !== NotebookCellsChangeType.Initialize) { + this._onDidChangeContent.fire(); + } })); + this._register(this._notebook.onDidChangeDirty(() => { this._onDidChangeDirty.fire(); })); diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 40e62b67084..99a754b4e55 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -41,13 +41,13 @@ export interface INotebookService { onDidChangeVisibleEditors: Event; onNotebookEditorAdd: Event; onNotebookEditorsRemove: Event; - onNotebookDocumentRemove: Event; - onNotebookDocumentAdd: Event; + onDidRemoveNotebookDocument: Event; + onDidAddNotebookDocument: Event; onNotebookDocumentSaved: Event; onDidChangeKernels: Event; onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelId: string | undefined }>; - registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): void; - unregisterNotebookProvider(viewType: string): void; + registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable; + transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]): void; transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]): void; registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable; diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts b/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts index b209d8c7fd4..7693c93ec69 100644 --- a/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl.ts @@ -113,7 +113,7 @@ export class NotebookEditorModelManager extends Disposable { const toDispose = new DisposableStore(); - // TODO, accept Model change + // TODO@rebornix, accept Model change // toDispose.add(model.onDidChangeContent((e) => { // this._proxy.acceptModelChanged(modelUrl.toString(), e); diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 5e2037d1a14..92eae8a9b4a 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -69,18 +69,18 @@ suite('NotebookViewModel', () => { ['//c', 'javascript', CellKind.Code, [], { editable: true }], ], (editor, viewModel) => { - viewModel.moveCellToIdx(0, 1, 0, false); + viewModel.moveCellToIdx(0, 1, 0, true); // no-op assert.equal(viewModel.viewCells[0].getText(), '//a'); assert.equal(viewModel.viewCells[1].getText(), '//b'); - viewModel.moveCellToIdx(0, 1, 1, false); + viewModel.moveCellToIdx(0, 1, 1, true); // b, a, c assert.equal(viewModel.viewCells[0].getText(), '//b'); assert.equal(viewModel.viewCells[1].getText(), '//a'); assert.equal(viewModel.viewCells[2].getText(), '//c'); - viewModel.moveCellToIdx(0, 1, 2, false); + viewModel.moveCellToIdx(0, 1, 2, true); // a, c, b assert.equal(viewModel.viewCells[0].getText(), '//a'); assert.equal(viewModel.viewCells[1].getText(), '//c'); @@ -100,12 +100,12 @@ suite('NotebookViewModel', () => { ['//c', 'javascript', CellKind.Code, [], { editable: true }], ], (editor, viewModel) => { - viewModel.moveCellToIdx(1, 1, 0, false); + viewModel.moveCellToIdx(1, 1, 0, true); // b, a, c assert.equal(viewModel.viewCells[0].getText(), '//b'); assert.equal(viewModel.viewCells[1].getText(), '//a'); - viewModel.moveCellToIdx(2, 1, 0, false); + viewModel.moveCellToIdx(2, 1, 0, true); // c, b, a assert.equal(viewModel.viewCells[0].getText(), '//c'); assert.equal(viewModel.viewCells[1].getText(), '//b'); diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index adb0abc97d0..31eab292f31 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -66,6 +66,9 @@ export class TestNotebookEditor implements INotebookEditor { constructor( ) { } + getSelectionHandles(): number[] { + return []; + } setOptions(options: NotebookEditorOptions | undefined): Promise { @@ -82,6 +85,7 @@ export class TestNotebookEditor implements INotebookEditor { onDidScroll = new Emitter().event; onWillDispose = new Emitter().event; onDidChangeVisibleRanges: Event = new Emitter().event; + onDidChangeSelection: Event = new Emitter().event; visibleRanges: ICellRange[] = []; uri?: URI | undefined; textModel?: NotebookTextModel | undefined; @@ -333,7 +337,7 @@ export class NotebookEditorTestModel extends EditorModel implements INotebookEdi ) { super(); - if (_notebook && _notebook.onDidChangeCells) { + if (_notebook && _notebook.onDidChangeContent) { this._register(_notebook.onDidChangeContent(() => { this._dirty = true; this._onDidChangeDirty.fire(); diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index 26fd54407ff..ed53348c28a 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -10,7 +10,7 @@ import { createCancelablePromise, TimeoutTimer } from 'vs/base/common/async'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { defaultGenerator } from 'vs/base/common/idGenerator'; -import { dispose, IDisposable, toDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { LRUCache } from 'vs/base/common/map'; import { escape } from 'vs/base/common/strings'; import 'vs/css!./outlinePane'; @@ -280,9 +280,9 @@ export class OutlinePane extends ViewPane { } dispose(): void { - dispose(this._disposables); - dispose(this._requestOracle); - dispose(this._editorDisposables); + this._disposables.dispose(); + this._requestOracle?.dispose(); + this._editorDisposables.dispose(); super.dispose(); } @@ -395,7 +395,7 @@ export class OutlinePane extends ViewPane { if (visible && !this._requestOracle) { this._requestOracle = this._instantiationService.createInstance(RequestOracle, (editor, event) => this._doUpdate(editor, event), DocumentSymbolProviderRegistry); } else if (!visible) { - dispose(this._requestOracle); + this._requestOracle?.dispose(); this._requestOracle = undefined; this._doUpdate(undefined, undefined); } @@ -495,7 +495,7 @@ export class OutlinePane extends ViewPane { this._progressBar.infinite().show(requestDelay); const createdModel = await OutlinePane._createOutlineModel(textModel, this._editorDisposables); - dispose(loadingMessage); + loadingMessage?.dispose(); if (!createdModel) { return; } diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index 337746000d6..262c391623d 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -35,7 +35,6 @@ import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { groupBy } from 'vs/base/common/arrays'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { editorBackground, selectBorder } from 'vs/platform/theme/common/colorRegistry'; -import { addClass } from 'vs/base/browser/dom'; import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export class OutputViewPane extends ViewPane { @@ -90,7 +89,7 @@ export class OutputViewPane extends ViewPane { renderBody(container: HTMLElement): void { super.renderBody(container); this.editor.create(container); - addClass(container, 'output-view'); + container.classList.add('output-view'); const codeEditor = this.editor.getControl(); codeEditor.setAriaOptions({ role: 'document', activeDescendant: undefined }); this._register(codeEditor.onDidChangeModelContent(() => { @@ -293,7 +292,7 @@ class SwitchOutputActionViewItem extends SelectActionViewItem { render(container: HTMLElement): void { super.render(container); - addClass(container, 'switch-output'); + container.classList.add('switch-output'); this._register(attachStylerCallback(this.themeService, { selectBorder }, colors => { container.style.borderColor = colors.selectBorder ? `${colors.selectBorder}` : ''; })); diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index fc27aa4c3ba..de11973bf42 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -358,7 +358,7 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP this._register(this.recordKeysAction.onDidChange(e => { if (e.checked !== undefined) { - DOM.toggleClass(recordingBadge, 'disabled', !e.checked); + recordingBadge.classList.toggle('disabled', !e.checked); if (e.checked) { this.searchWidget.inputBox.setPlaceHolder(keybindingsSearchPlaceholder); this.searchWidget.inputBox.setAriaLabel(keybindingsSearchPlaceholder); @@ -424,7 +424,7 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP private layoutSearchWidget(dimension: DOM.Dimension): void { this.searchWidget.layout(dimension); - DOM.toggleClass(this.headerContainer, 'small', dimension.width < 400); + this.headerContainer.classList.toggle('small', dimension.width < 400); this.searchWidget.inputBox.inputElement.style.paddingRight = `${DOM.getTotalWidth(this.actionsContainer) + 12}px`; } @@ -474,10 +474,10 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP this._register(this.keybindingsList.onContextMenu(e => this.onContextMenu(e))); this._register(this.keybindingsList.onDidChangeFocus(e => this.onFocusChange(e))); this._register(this.keybindingsList.onDidFocus(() => { - DOM.addClass(this.keybindingsList.getHTMLElement(), 'focused'); + this.keybindingsList.getHTMLElement().classList.add('focused'); })); this._register(this.keybindingsList.onDidBlur(() => { - DOM.removeClass(this.keybindingsList.getHTMLElement(), 'focused'); + this.keybindingsList.getHTMLElement().classList.remove('focused'); this.keybindingFocusContextKey.reset(); })); this._register(this.keybindingsList.onMouseDblClick(() => { @@ -821,7 +821,7 @@ class KeybindingItemRenderer implements IListRenderer s.key === current.key) : + navigatorSettings.findIndex(s => s.key === current.key) : -1; this._settingsNavigator = new SettingsNavigator(navigatorSettings, Math.max(currentIndex, 0)); @@ -691,7 +690,7 @@ class PreferencesRenderersController extends Disposable { if (nlpMetadata) { const sortedKeys = Object.keys(nlpMetadata.scoredResults).sort((a, b) => nlpMetadata.scoredResults[b].score - nlpMetadata.scoredResults[a].score); const suffix = '##' + key; - data['nlpIndex'] = arrays.firstIndex(sortedKeys, key => key.endsWith(suffix)); + data['nlpIndex'] = sortedKeys.findIndex(key => key.endsWith(suffix)); } const settingLocation = this._findSetting(this.lastFilterResult, key); @@ -791,7 +790,7 @@ class SideBySidePreferencesWidget extends Widget { ) { super(); - DOM.addClass(parentElement, 'side-by-side-preferences-editor'); + parentElement.classList.add('side-by-side-preferences-editor'); this.splitview = new SplitView(parentElement, { orientation: Orientation.HORIZONTAL }); this._register(this.splitview); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index eb525604877..3829f930aad 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -31,7 +31,6 @@ import { DefaultSettingsEditorModel, SettingsEditorModel, WorkspaceConfiguration import { IMarkerService, IMarkerData, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { find } from 'vs/base/common/arrays'; export interface IPreferencesRenderer extends IDisposable { readonly preferencesModel: IPreferencesEditorModel; @@ -321,7 +320,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR const { key, overrideOf } = setting; if (overrideOf) { const setting = this.getSetting(overrideOf); - return find(setting!.overrides!, override => override.key === key); + return setting!.overrides!.find(override => override.key === key); } const settingsGroups = this.filterResult ? this.filterResult.filteredGroups : this.preferencesModel.settingsGroups; return this.getPreference(key, settingsGroups); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 592600feba1..97c53d12e14 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -192,15 +192,15 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { } toggleCollapse(collapse: boolean) { - DOM.toggleClass(this.titleContainer, 'collapsed', collapse); + this.titleContainer.classList.toggle('collapsed', collapse); } toggleFocus(focus: boolean): void { - DOM.toggleClass(this.titleContainer, 'focused', focus); + this.titleContainer.classList.toggle('focused', focus); } isCollapsed(): boolean { - return DOM.hasClass(this.titleContainer, 'collapsed'); + return this.titleContainer.classList.contains('collapsed'); } private layout(): void { @@ -256,7 +256,7 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { private collapse(collapse: boolean) { if (collapse !== this.isCollapsed()) { - DOM.toggleClass(this.titleContainer, 'collapsed', collapse); + this.titleContainer.classList.toggle('collapsed', collapse); this._onToggled.fire(collapse); } } @@ -412,17 +412,17 @@ export class FolderSettingsActionViewItem extends BaseActionViewItem { this.anchorElement.title = (await this.preferencesService.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, this._folder.uri))?.fsPath || ''; const detailsText = this.labelWithCount(this._action.label, total); this.detailsElement.textContent = detailsText; - DOM.toggleClass(this.dropDownElement, 'hide', workspace.folders.length === 1 || !this._action.checked); + this.dropDownElement.classList.toggle('hide', workspace.folders.length === 1 || !this._action.checked); } else { const labelText = this.labelWithCount(this._action.label, total); this.labelElement.textContent = labelText; this.detailsElement.textContent = ''; this.anchorElement.title = this._action.label; - DOM.removeClass(this.dropDownElement, 'hide'); + this.dropDownElement.classList.remove('hide'); } - DOM.toggleClass(this.anchorElement, 'checked', this._action.checked); - DOM.toggleClass(this.container, 'disabled', !this._action.enabled); + this.anchorElement.classList.toggle('checked', this._action.checked); + this.container.classList.toggle('disabled', !this._action.enabled); } private showMenu(): void { @@ -595,7 +595,7 @@ export class SettingsTargetsWidget extends Widget { } private async update(): Promise { - DOM.toggleClass(this.settingsSwitcherBar.domNode, 'empty-workbench', this.contextService.getWorkbenchState() === WorkbenchState.EMPTY); + this.settingsSwitcherBar.domNode.classList.toggle('empty-workbench', this.contextService.getWorkbenchState() === WorkbenchState.EMPTY); this.userRemoteSettings.enabled = !!(this.options.enableRemoteSettings && this.environmentService.configuration.remoteAuthority); this.workspaceSettings.enabled = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY; this.folderSettings.getAction().enabled = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && this.contextService.getWorkspace().folders.length > 0; @@ -697,13 +697,13 @@ export class SearchWidget extends Widget { layout(dimension: DOM.Dimension) { if (dimension.width < 400) { if (this.countElement) { - DOM.addClass(this.countElement, 'hide'); + this.countElement.classList.add('hide'); } this.inputBox.inputElement.style.paddingRight = '0px'; } else { if (this.countElement) { - DOM.removeClass(this.countElement, 'hide'); + this.countElement.classList.remove('hide'); } this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px'; diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index fb51919e9b1..c6dfe890b38 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -12,7 +12,7 @@ import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry, registerActi import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar'; import { ILabelService } from 'vs/platform/label/common/label'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -21,7 +21,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { RemoteConnectionState } from 'vs/workbench/browser/contextkeys'; import { isWeb } from 'vs/base/common/platform'; import { once } from 'vs/base/common/functional'; @@ -38,7 +37,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr private remoteAuthority = this.environmentService.configuration.remoteAuthority; private connectionState: 'initializing' | 'connected' | 'disconnected' | undefined = undefined; - private connectionStateContextKey = RemoteConnectionState.bindTo(this.contextKeyService); + private connectionStateContextKey = new RawContextKey<'' | 'initializing' | 'disconnected' | 'connected'>('remoteConnectionState', '').bindTo(this.contextKeyService); constructor( @IStatusbarService private readonly statusbarService: IStatusbarService, 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 365ae95ec0e..52c5305bc26 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -27,9 +27,8 @@ import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteA import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys'; import { IDownloadService } from 'vs/platform/download/common/download'; -import { OpenLocalFileFolderCommand, OpenLocalFileCommand, OpenLocalFolderCommand, SaveLocalFileCommand } from 'vs/workbench/services/dialogs/browser/simpleFileDialog'; +import { OpenLocalFileFolderCommand, OpenLocalFileCommand, OpenLocalFolderCommand, SaveLocalFileCommand, RemoteFileDialogContext } from 'vs/workbench/services/dialogs/browser/simpleFileDialog'; class RemoteChannelsContribution implements IWorkbenchContribution { diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index f2cec6d0813..70911617807 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -22,7 +22,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { MenuItemAction, IMenuService } from 'vs/platform/actions/common/actions'; import { IAction, IActionViewItem, ActionRunner, Action, RadioGroup, Separator, SubmenuAction, IActionViewItemProvider } from 'vs/base/common/actions'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IThemeService, LIGHT, registerThemingParticipant, IFileIconTheme } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, IFileIconTheme } from 'vs/platform/theme/common/themeService'; import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, collectContextMenuActions, StatusBarAction, StatusBarActionViewItem, getRepositoryVisibilityActions } from './util'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { WorkbenchCompressibleObjectTree, IOpenEvent } from 'vs/platform/list/browser/listService'; @@ -75,6 +75,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { RepositoryRenderer } from 'vs/workbench/contrib/scm/browser/scmRepositoryRenderer'; import { IPosition } from 'vs/editor/common/core/position'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; type TreeElement = ISCMRepository | ISCMInput | ISCMResourceGroup | IResourceNode | ISCMResource; @@ -406,7 +407,7 @@ class ResourceRenderer implements ICompressibleTreeRenderer { const theme = this.themeService.getColorTheme(); - const icon = iconResource && (theme.type === LIGHT ? iconResource.decorations.icon : iconResource.decorations.iconDark); + const icon = iconResource && (theme.type === ColorScheme.LIGHT ? iconResource.decorations.icon : iconResource.decorations.iconDark); template.fileLabel.setFile(uri, { fileDecorations: { colors: false, badges: !icon }, diff --git a/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts b/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts index 1b21a410386..db12da69bef 100644 --- a/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts +++ b/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts @@ -8,7 +8,7 @@ import { RawContextKey, IContextKeyService, ContextKeyExpr, IContextKey } from ' import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ISnippetsService } from './snippets.contribution'; import { getNonWhitespacePrefix } from './snippetsService'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { Range } from 'vs/editor/common/core/range'; import { registerEditorContribution, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; @@ -53,8 +53,8 @@ export class TabCompletionController implements IEditorContribution { } dispose(): void { - dispose(this._configListener); - dispose(this._selectionListener); + this._configListener.dispose(); + this._selectionListener?.dispose(); } private _update(): void { @@ -62,7 +62,7 @@ export class TabCompletionController implements IEditorContribution { if (this._enabled !== enabled) { this._enabled = enabled; if (!this._enabled) { - dispose(this._selectionListener); + this._selectionListener?.dispose(); } else { this._selectionListener = this._editor.onDidChangeCursorSelection(e => this._updateSnippets()); if (this._editor.getModel()) { diff --git a/src/vs/workbench/contrib/terminal/test/common/terminalColorRegistry.test.ts b/src/vs/workbench/contrib/terminal/test/common/terminalColorRegistry.test.ts index 48c52ffb3a8..9854f914413 100644 --- a/src/vs/workbench/contrib/terminal/test/common/terminalColorRegistry.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/terminalColorRegistry.test.ts @@ -7,13 +7,14 @@ import * as assert from 'assert'; import { Extensions as ThemeingExtensions, IColorRegistry, ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ansiColorIdentifiers, registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; -import { IColorTheme, ThemeType } from 'vs/platform/theme/common/themeService'; +import { IColorTheme } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; registerColors(); let themingRegistry = Registry.as(ThemeingExtensions.ColorContribution); -function getMockTheme(type: ThemeType): IColorTheme { +function getMockTheme(type: ColorScheme): IColorTheme { let theme = { selector: '', label: '', @@ -30,7 +31,7 @@ function getMockTheme(type: ThemeType): IColorTheme { suite('Workbench - TerminalColorRegistry', () => { test('hc colors', function () { - let theme = getMockTheme('hc'); + let theme = getMockTheme(ColorScheme.HIGH_CONTRAST); let colors = ansiColorIdentifiers.map(colorId => Color.Format.CSS.formatHexA(theme.getColor(colorId)!, true)); assert.deepEqual(colors, [ @@ -55,7 +56,7 @@ suite('Workbench - TerminalColorRegistry', () => { }); test('light colors', function () { - let theme = getMockTheme('light'); + let theme = getMockTheme(ColorScheme.LIGHT); let colors = ansiColorIdentifiers.map(colorId => Color.Format.CSS.formatHexA(theme.getColor(colorId)!, true)); assert.deepEqual(colors, [ @@ -80,7 +81,7 @@ suite('Workbench - TerminalColorRegistry', () => { }); test('dark colors', function () { - let theme = getMockTheme('dark'); + let theme = getMockTheme(ColorScheme.DARK); let colors = ansiColorIdentifiers.map(colorId => Color.Format.CSS.formatHexA(theme.getColor(colorId)!, true)); assert.deepEqual(colors, [ diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index ff3a71ec746..dca30b971db 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -17,7 +17,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IColorRegistry, Extensions as ColorRegistryExtensions } from 'vs/platform/theme/common/colorRegistry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Color } from 'vs/base/common/color'; -import { LIGHT, DARK, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; import { colorThemeSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IQuickInputService, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; @@ -45,9 +45,9 @@ export class SelectColorThemeAction extends Action { const currentTheme = this.themeService.getColorTheme(); const picks: QuickPickInput[] = [ - ...toEntries(themes.filter(t => t.type === LIGHT), localize('themes.category.light', "light themes")), - ...toEntries(themes.filter(t => t.type === DARK), localize('themes.category.dark', "dark themes")), - ...toEntries(themes.filter(t => t.type === HIGH_CONTRAST), localize('themes.category.hc', "high contrast themes")), + ...toEntries(themes.filter(t => t.type === ColorScheme.LIGHT), localize('themes.category.light', "light themes")), + ...toEntries(themes.filter(t => t.type === ColorScheme.DARK), localize('themes.category.dark', "dark themes")), + ...toEntries(themes.filter(t => t.type === ColorScheme.HIGH_CONTRAST), localize('themes.category.hc', "high contrast themes")), ...configurationEntries(this.extensionGalleryService, localize('installColorThemes', "Install Additional Color Themes...")) ]; diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 8ba8fc64af5..5eab72c696f 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -32,7 +32,7 @@ import { ITimelineService, TimelineChangeEvent, TimelineItem, TimelineOptions, T import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { SideBySideEditor, toResource } from 'vs/workbench/common/editor'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IThemeService, LIGHT, ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -41,6 +41,7 @@ import { MenuEntryActionViewItem, createAndFillInContextMenuActions, SubmenuEntr import { MenuItemAction, IMenuService, MenuId, registerAction2, Action2, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; const ItemHeight = 22; @@ -1117,7 +1118,7 @@ class TimelineTreeRenderer implements ITreeRenderer, actionRunner: MultipleSelectionActionRunner): void { + this.hoverService.hideHover(); const node: ITreeItem | null = treeEvent.element; if (node === null) { return; @@ -754,7 +757,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer { + this.viewService.openView(this.id, !preserveFocus); + } + }; + + await this.webviewViewService.resolve(this.id, webviewView, source.token); }); } } diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts index 5de370ac42d..60d4a3a7a6f 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts @@ -13,11 +13,14 @@ export const IWebviewViewService = createDecorator('webview export interface WebviewView { title?: string; + description?: string; readonly webview: WebviewOverlay; readonly onDidChangeVisibility: Event; readonly onDispose: Event; + + show(preserveFocus: boolean): void; } export interface IWebviewViewResolver { diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index 47e1fdcf5d5..52d61bf7087 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -206,7 +206,7 @@ class DesktopMain extends Disposable { fileService.registerProvider(Schemas.file, diskFileSystemProvider); // User Data Provider - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(this.environmentService.appSettingsHome, this.environmentService.backupHome, diskFileSystemProvider, this.environmentService, logService)); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(this.environmentService.appSettingsHome, this.environmentService.configuration.backupPath ? URI.file(this.environmentService.configuration.backupPath) : undefined, diskFileSystemProvider, this.environmentService, logService)); const connection = remoteAgentService.getConnection(); if (connection) { @@ -259,8 +259,8 @@ class DesktopMain extends Disposable { // Fallback to empty workspace if we have no payload yet. if (!workspaceInitializationPayload) { let id: string; - if (this.environmentService.configuration.backupWorkspaceResource) { - id = basename(this.environmentService.configuration.backupWorkspaceResource); // we know the backupPath must be a unique path so we leverage its name as workspace ID + if (this.environmentService.backupWorkspaceHome) { + id = basename(this.environmentService.backupWorkspaceHome); // we know the backupPath must be a unique path so we leverage its name as workspace ID } else if (this.environmentService.isExtensionDevelopment) { id = 'ext-dev'; // extension development window never stores backups and is a singleton } else { diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 07fe262c796..38ccac63b5c 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -9,7 +9,7 @@ import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; +import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { ToggleDevToolsAction, ConfigureRuntimeArgumentsAction } from 'vs/workbench/electron-sandbox/actions/developerActions'; import { ZoomResetAction, ZoomOutAction, ZoomInAction, CloseCurrentWindowAction, SwitchWindow, QuickSwitchWindow, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-sandbox/actions/windowActions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -17,7 +17,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IsDevelopmentContext, IsMacContext } from 'vs/platform/contextkey/common/contextkeys'; -import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; +import { EditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import product from 'vs/platform/product/common/product'; @@ -45,7 +45,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: CloseCurrentWindowAction.ID, // close the window when the last editor is closed by reusing the same keybinding weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(NoEditorsVisibleContext, SingleEditorGroupsContext), + when: ContextKeyExpr.and(EditorsVisibleContext.toNegated(), SingleEditorGroupsContext), primary: KeyMod.CtrlCmd | KeyCode.KEY_W, handler: accessor => { const electronService = accessor.get(IElectronService); @@ -255,13 +255,6 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; 'default': false, 'description': nls.localize('closeWhenEmpty', "Controls whether closing the last editor should also close the window. This setting only applies for windows that do not show folders.") }, - 'window.autoDetectHighContrast': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('autoDetectHighContrast', "If enabled, will automatically change to high contrast theme if the OS is using a high contrast theme, and to dark theme when switching away from a high contrast theme."), - 'scope': ConfigurationScope.APPLICATION, - 'included': isWindows || isMacintosh - }, 'window.doubleClickIconToClose': { 'type': 'boolean', 'default': false, diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 11a57edd768..9d53f97e8fb 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -181,7 +181,7 @@ class DesktopMain extends Disposable { fileService.registerProvider(Schemas.file, simpleFileSystemProvider); // User Data Provider - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(URI.file('user-home'), this.environmentService.backupHome, simpleFileSystemProvider, this.environmentService, logService)); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(URI.file('user-home'), undefined, simpleFileSystemProvider, this.environmentService, logService)); const connection = remoteAgentService.getConnection(); if (connection) { diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index 8dbebb9eef7..1159fe856e7 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -91,7 +91,7 @@ export class SimpleWorkbenchEnvironmentService implements INativeWorkbenchEnviro get serviceMachineIdResource(): URI { return joinPath(this.userRoamingDataHome, 'machineid'); } get userDataSyncLogResource(): URI { return joinPath(this.userRoamingDataHome, 'syncLog'); } get userDataSyncHome(): URI { return joinPath(this.userRoamingDataHome, 'syncHome'); } - get backupHome(): URI { return joinPath(this.userRoamingDataHome, 'backupsHome'); } + get backupWorkspaceHome(): URI { return joinPath(this.userRoamingDataHome, 'Backups', 'workspace'); } options?: IWorkbenchConstructionOptions | undefined; logExtensionHostCommunication?: boolean | undefined; @@ -120,6 +120,7 @@ export class SimpleWorkbenchEnvironmentService implements INativeWorkbenchEnviro appSettingsHome: URI = undefined!; userDataPath: string = undefined!; machineSettingsResource: URI = undefined!; + backupHome: string = undefined!; backupWorkspacesPath: string = undefined!; log?: string | undefined; diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 3e0a8c48fcd..59270cd9ddb 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -13,7 +13,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { toResource, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest, ColorScheme } from 'vs/platform/windows/common/windows'; +import { IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest } from 'vs/platform/windows/common/windows'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { applyZoom } from 'vs/platform/windows/electron-sandbox/window'; @@ -198,12 +198,6 @@ export class NativeWindow extends Disposable { setFullscreen(false); }); - // High Contrast Events - this._register(this.electronService.onColorSchemeChange(async scheme => { - await this.lifecycleService.when(LifecyclePhase.Ready); - this.themeService.setOSHighContrast(scheme === ColorScheme.HIGH_CONTRAST); - })); - // accessibility support changed event ipcRenderer.on('vscode:accessibilitySupportChanged', (event: unknown, accessibilitySupportEnabled: boolean) => { this.accessibilityService.setAccessibilitySupport(accessibilitySupportEnabled ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled); diff --git a/src/vs/workbench/services/backup/common/backupFileService.ts b/src/vs/workbench/services/backup/common/backupFileService.ts index 71fcefaae3a..175d5e9ecca 100644 --- a/src/vs/workbench/services/backup/common/backupFileService.ts +++ b/src/vs/workbench/services/backup/common/backupFileService.ts @@ -128,7 +128,7 @@ export class BackupFileService implements IBackupFileService { } private initialize(): BackupFileServiceImpl | InMemoryBackupFileService { - const backupWorkspaceResource = this.environmentService.configuration.backupWorkspaceResource; + const backupWorkspaceResource = this.environmentService.backupWorkspaceHome; if (backupWorkspaceResource) { return new BackupFileServiceImpl(backupWorkspaceResource, this.hashPath, this.fileService, this.logService); } @@ -140,7 +140,7 @@ export class BackupFileService implements IBackupFileService { // Re-init implementation (unless we are running in-memory) if (this.impl instanceof BackupFileServiceImpl) { - const backupWorkspaceResource = this.environmentService.configuration.backupWorkspaceResource; + const backupWorkspaceResource = this.environmentService.backupWorkspaceHome; if (backupWorkspaceResource) { this.impl.initialize(backupWorkspaceResource); } else { diff --git a/src/vs/workbench/services/backup/electron-sandbox/backup.ts b/src/vs/workbench/services/backup/electron-sandbox/backup.ts deleted file mode 100644 index 4e1c308b73b..00000000000 --- a/src/vs/workbench/services/backup/electron-sandbox/backup.ts +++ /dev/null @@ -1,12 +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 } from 'vs/base/common/uri'; -import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; -import { joinPath, relativePath } from 'vs/base/common/resources'; - -export function toBackupWorkspaceResource(backupWorkspacePath: string, environmentService: INativeWorkbenchEnvironmentService): URI { - return joinPath(environmentService.userRoamingDataHome, relativePath(URI.file(environmentService.userDataPath), URI.file(backupWorkspacePath))!); -} 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 94dfd77136f..4da557d1732 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 @@ -24,13 +24,11 @@ import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environ import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { IFileService } from 'vs/platform/files/common/files'; import { hashPath, BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; -import { BACKUPS } from 'vs/platform/environment/common/environment'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { VSBuffer } from 'vs/base/common/buffer'; import { TestWorkbenchConfiguration } from 'vs/workbench/test/electron-browser/workbenchTestServices'; const userdataDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice'); -const appSettingsHome = path.join(userdataDir, 'User'); const backupHome = path.join(userdataDir, 'Backups'); const workspacesJsonPath = path.join(backupHome, 'workspaces.json'); @@ -67,7 +65,7 @@ export class NodeTestBackupFileService extends BackupFileService { const fileService = new FileService(logService); const diskFileSystemProvider = new DiskFileSystemProvider(logService); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService, logService)); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, URI.file(workspaceBackupPath), diskFileSystemProvider, environmentService, logService)); super(environmentService, fileService, logService); @@ -159,7 +157,7 @@ suite('BackupFileService', () => { const backupResource = fooFile; const workspaceHash = hashPath(workspaceResource); const filePathHash = hashPath(backupResource); - const expectedPath = URI.file(path.join(appSettingsHome, BACKUPS, workspaceHash, Schemas.file, filePathHash)).with({ scheme: Schemas.userData }).toString(); + const expectedPath = URI.file(path.join(backupHome, workspaceHash, Schemas.file, filePathHash)).with({ scheme: Schemas.userData }).toString(); assert.equal(service.toBackupResource(backupResource).toString(), expectedPath); }); @@ -168,7 +166,7 @@ suite('BackupFileService', () => { const backupResource = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); const workspaceHash = hashPath(workspaceResource); const filePathHash = hashPath(backupResource); - const expectedPath = URI.file(path.join(appSettingsHome, BACKUPS, workspaceHash, Schemas.untitled, filePathHash)).with({ scheme: Schemas.userData }).toString(); + const expectedPath = URI.file(path.join(backupHome, workspaceHash, Schemas.untitled, filePathHash)).with({ scheme: Schemas.userData }).toString(); assert.equal(service.toBackupResource(backupResource).toString(), expectedPath); }); }); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index d748536584c..ee38ee00689 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -7,7 +7,6 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import * as json from 'vs/base/common/json'; -import * as strings from 'vs/base/common/strings'; import { setProperty } from 'vs/base/common/jsonEdit'; import { Queue } from 'vs/base/common/async'; import { Edit } from 'vs/base/common/jsonFormatter'; @@ -426,7 +425,7 @@ export class ConfigurationEditingService { // Without jsonPath, the entire configuration file is being replaced, so we just use JSON.stringify if (!jsonPath.length) { - const content = JSON.stringify(value, null, insertSpaces ? strings.repeat(' ', tabSize) : '\t'); + const content = JSON.stringify(value, null, insertSpaces ? ' '.repeat(tabSize) : '\t'); return [{ content, length: model.getValue().length, diff --git a/src/vs/workbench/services/configuration/common/jsonEditingService.ts b/src/vs/workbench/services/configuration/common/jsonEditingService.ts index 7aff6207f5b..a9686fae2d9 100644 --- a/src/vs/workbench/services/configuration/common/jsonEditingService.ts +++ b/src/vs/workbench/services/configuration/common/jsonEditingService.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as json from 'vs/base/common/json'; -import * as strings from 'vs/base/common/strings'; import { setProperty } from 'vs/base/common/jsonEdit'; import { Queue } from 'vs/base/common/async'; import { Edit } from 'vs/base/common/jsonFormatter'; @@ -79,7 +78,7 @@ export class JSONEditingService implements IJSONEditingService { // With empty path the entire file is being replaced, so we just use JSON.stringify if (!path.length) { - const content = JSON.stringify(value, null, insertSpaces ? strings.repeat(' ', tabSize) : '\t'); + const content = JSON.stringify(value, null, insertSpaces ? ' '.repeat(tabSize) : '\t'); return [{ content, length: content.length, diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts index 0e651c0bee7..40e66c9b1f4 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts @@ -110,7 +110,7 @@ suite('ConfigurationEditingService', () => { const fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IRemoteAgentService, remoteAgentService); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService); diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index 290cd1de055..e04efb9e339 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -113,7 +113,7 @@ suite('WorkspaceContextService - Folder', () => { const fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, new DiskFileSystemProvider(new NullLogService()), environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, new DiskFileSystemProvider(new NullLogService()), environmentService, new NullLogService())); workspaceContextService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService(environmentService, { _serviceBrand: undefined, ...product }, new RemoteAuthorityResolverService(), new SignService(undefined), new NullLogService())); return (workspaceContextService).initialize(convertToWorkspacePayload(URI.file(folderDir))); }); @@ -179,7 +179,7 @@ suite('WorkspaceContextService - Workspace', () => { const fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); @@ -239,7 +239,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { const fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); @@ -500,7 +500,7 @@ suite('WorkspaceService - Initialization', () => { const fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); @@ -777,7 +777,7 @@ suite('WorkspaceConfigurationService - Folder', () => { fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); workspaceService = disposableStore.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService)); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); @@ -1286,7 +1286,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { const fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); @@ -1888,7 +1888,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve() }; testObject = new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, fileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, testObject); diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 46801bd7349..cbefdbce353 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -21,12 +21,11 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { Schemas } from 'vs/base/common/network'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { equalsIgnoreCase, format, startsWithIgnoreCase } from 'vs/base/common/strings'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { isValidBasename } from 'vs/base/common/extpath'; -import { RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys'; import { Emitter } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { createCancelablePromise, CancelablePromise } from 'vs/base/common/async'; @@ -98,6 +97,8 @@ enum UpdateResult { InvalidPath } +export const RemoteFileDialogContext = new RawContextKey('remoteFileDialogVisible', false); + export class SimpleFileDialog { private options!: IOpenDialogOptions; private currentFolder!: URI; diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 51d4e2d2384..06e2baf7063 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -7,21 +7,21 @@ import { Schemas } from 'vs/base/common/network'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; -import { BACKUPS, IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; -import { ColorScheme, IPath } from 'vs/platform/windows/common/windows'; -import { IWorkbenchEnvironmentService, IWorkbenchConfiguration } from 'vs/workbench/services/environment/common/environmentService'; +import { IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; +import { IPath, IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchConstructionOptions as IWorkbenchOptions } from 'vs/workbench/workbench.web.api'; import product from 'vs/platform/product/common/product'; import { memoize } from 'vs/base/common/decorators'; import { onUnexpectedError } from 'vs/base/common/errors'; import { parseLineAndColumnAware } from 'vs/base/common/extpath'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; -class BrowserWorkbenchConfiguration implements IWorkbenchConfiguration { +class BrowserWorkbenchConfiguration implements IWindowConfiguration { constructor( private readonly options: IBrowserWorkbenchOptions, - private readonly payload: Map | undefined, - private readonly backupHome: URI + private readonly payload: Map | undefined ) { } @memoize @@ -30,9 +30,6 @@ class BrowserWorkbenchConfiguration implements IWorkbenchConfiguration { @memoize get remoteAuthority(): string | undefined { return this.options.remoteAuthority; } - @memoize - get backupWorkspaceResource(): URI { return joinPath(this.backupHome, this.options.workspaceId); } - @memoize get filesToOpenOrCreate(): IPath[] | undefined { if (this.payload) { @@ -80,7 +77,7 @@ class BrowserWorkbenchConfiguration implements IWorkbenchConfiguration { return ColorScheme.DARK; } - return ColorScheme.DEFAULT; + return ColorScheme.LIGHT; } } @@ -101,10 +98,10 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment declare readonly _serviceBrand: undefined; - private _configuration: IWorkbenchConfiguration | undefined = undefined; - get configuration(): IWorkbenchConfiguration { + private _configuration: IWindowConfiguration | undefined = undefined; + get configuration(): IWindowConfiguration { if (!this._configuration) { - this._configuration = new BrowserWorkbenchConfiguration(this.options, this.payload, this.backupHome); + this._configuration = new BrowserWorkbenchConfiguration(this.options, this.payload); } return this._configuration; @@ -163,7 +160,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment get keyboardLayoutResource(): URI { return joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); } @memoize - get backupHome(): URI { return joinPath(this.userRoamingDataHome, BACKUPS); } + get backupWorkspaceHome(): URI { return joinPath(this.userRoamingDataHome, 'Backups', this.options.workspaceId); } @memoize get untitledWorkspacesHome(): URI { return joinPath(this.userRoamingDataHome, 'Workspaces'); } diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index 159b5c5aeef..01feb213573 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -11,19 +11,25 @@ import { URI } from 'vs/base/common/uri'; export const IWorkbenchEnvironmentService = createDecorator('environmentService'); -export interface IWorkbenchConfiguration extends IWindowConfiguration { - backupWorkspaceResource?: URI; -} - +/** + * A workbench specific environment service that is only present in workbench + * layer. + */ export interface IWorkbenchEnvironmentService extends IEnvironmentService { + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE. AS SUCH: + // - PUT NON-WEB PROPERTIES INTO NATIVE WB ENV SERVICE + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + readonly _serviceBrand: undefined; - readonly configuration: IWorkbenchConfiguration; + readonly configuration: IWindowConfiguration; readonly options?: IWorkbenchOptions; readonly logFile: URI; + readonly backupWorkspaceHome?: URI; readonly logExtensionHostCommunication?: boolean; readonly extensionEnabledProposedApi?: string[]; @@ -33,4 +39,9 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { readonly webviewCspSource: string; readonly skipReleaseNotes: boolean; + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE. AS SUCH: + // - PUT NON-WEB PROPERTIES INTO NATIVE WB ENV SERVICE + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! } diff --git a/src/vs/workbench/services/environment/electron-browser/environmentService.ts b/src/vs/workbench/services/environment/electron-browser/environmentService.ts index c48430e0f9b..31b2a7a16ac 100644 --- a/src/vs/workbench/services/environment/electron-browser/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-browser/environmentService.ts @@ -8,7 +8,6 @@ import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } fro import { memoize } from 'vs/base/common/decorators'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; -import { toBackupWorkspaceResource } from 'vs/workbench/services/backup/electron-sandbox/backup'; import { dirname, join } from 'vs/base/common/path'; import product from 'vs/platform/product/common/product'; import { isLinux, isWindows } from 'vs/base/common/platform'; @@ -33,6 +32,9 @@ export class NativeWorkbenchEnvironmentService extends EnvironmentService implem @memoize get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.userData }); } + // Do not memoize as `backupPath` can change in configuration + get backupWorkspaceHome(): URI | undefined { return this.configuration.backupPath ? URI.file(this.configuration.backupPath).with({ scheme: this.userRoamingDataHome.scheme }) : undefined; } + @memoize get logFile(): URI { return URI.file(join(this.logsPath, `renderer${this.configuration.windowId}.log`)); } @@ -66,8 +68,6 @@ export class NativeWorkbenchEnvironmentService extends EnvironmentService implem readonly configuration: INativeWorkbenchConfiguration ) { super(configuration); - - this.configuration.backupWorkspaceResource = this.configuration.backupPath ? toBackupWorkspaceResource(this.configuration.backupPath, this) : undefined; } private doGetCLIPath(): string { diff --git a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts index bde5abaeb38..b75dd5fdd9e 100644 --- a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts @@ -3,13 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchConfiguration, IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { INativeWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { INativeWindowConfiguration, IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { URI } from 'vs/base/common/uri'; -export interface INativeWorkbenchConfiguration extends IWorkbenchConfiguration, INativeWindowConfiguration { } +export interface INativeWorkbenchConfiguration extends IWindowConfiguration, INativeWindowConfiguration { } +/** + * A subclass of the `IWorkbenchEnvironmentService` to be used only in native + * environments (Windows, Linux, macOS) but not e.g. web. + */ export interface INativeWorkbenchEnvironmentService extends IWorkbenchEnvironmentService, INativeEnvironmentService { readonly configuration: INativeWorkbenchConfiguration; diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index 79ba1b68a98..02ddb58cf37 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -19,7 +19,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { assign } from 'vs/base/common/objects'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { productService } from 'vs/workbench/test/browser/workbenchTestServices'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; @@ -608,11 +607,12 @@ function aLocalExtension(id: string, contributes?: IExtensionContributions, type function aLocalExtension2(id: string, manifest: any = {}, properties: any = {}): ILocalExtension { const [publisher, name] = id.split('.'); - properties = assign({ + manifest = { name, publisher, ...manifest }; + properties = { identifier: { id }, galleryIdentifier: { id, uuid: undefined }, - type: ExtensionType.User - }, properties); - manifest = assign({ name, publisher }, manifest); + type: ExtensionType.User, + ...properties + }; return Object.create({ manifest, ...properties }); } diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index 6b672330aba..8740402bd1c 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -28,6 +28,7 @@ import { localize } from 'vs/nls'; import { generateUuid } from 'vs/base/common/uuid'; import { canceled, onUnexpectedError } from 'vs/base/common/errors'; import { WEB_WORKER_IFRAME } from 'vs/workbench/services/extensions/common/webWorkerIframe'; +import { Barrier } from 'vs/base/common/async'; export interface IWebWorkerExtensionHostInitData { readonly autoStart: boolean; @@ -82,7 +83,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost return this._protocolPromise; } - private _startInsideIframe(): Promise { + private async _startInsideIframe(): Promise { const emitter = this._register(new Emitter()); const iframe = document.createElement('iframe'); @@ -111,6 +112,9 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost const iframeContent = `data:text/html;charset=utf-8,${encodeURIComponent(html)}`; iframe.setAttribute('src', iframeContent); + const barrier = new Barrier(); + let port!: MessagePort; + this._register(dom.addDisposableListener(window, 'message', (event) => { if (event.source !== iframe.contentWindow) { return; @@ -129,38 +133,68 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost return; } const { data } = event.data; + if (barrier.isOpen() || !(data instanceof MessagePort)) { + console.warn('UNEXPECTED message', event); + this._onDidExit.fire([81, 'UNEXPECTED message']); + return; + } + port = data; + barrier.open(); + })); + + document.body.appendChild(iframe); + this._register(toDisposable(() => iframe.remove())); + + // await MessagePort and use it to directly communicate + // with the worker extension host + await barrier.wait(); + + port.onmessage = (event) => { + const { data } = event; if (!(data instanceof ArrayBuffer)) { console.warn('UNKNOWN data received', data); this._onDidExit.fire([77, 'UNKNOWN data received']); return; } emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); - })); + }; const protocol: IMessagePassingProtocol = { onMessage: emitter.event, send: vsbuf => { const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); - iframe.contentWindow!.postMessage({ - vscodeWebWorkerExtHostId, - data: data - }, '*', [data]); + port.postMessage(data, [data]); } }; - document.body.appendChild(iframe); - this._register(toDisposable(() => iframe.remove())); - return this._performHandshake(protocol); } - private _startOutsideIframe(): Promise { + private async _startOutsideIframe(): Promise { const emitter = new Emitter(); const url = getWorkerBootstrapUrl(require.toUrl('../worker/extensionHostWorkerMain.js'), 'WorkerExtensionHost'); const worker = new Worker(url, { name: 'WorkerExtensionHost' }); + const barrier = new Barrier(); + let port!: MessagePort; + worker.onmessage = (event) => { + const { data } = event; + if (barrier.isOpen() || !(data instanceof MessagePort)) { + console.warn('UNEXPECTED message', event); + this._onDidExit.fire([81, 'UNEXPECTED message']); + return; + } + port = data; + barrier.open(); + }; + + // await MessagePort and use it to directly communicate + // with the worker extension host + await barrier.wait(); + + port.onmessage = (event) => { const { data } = event; if (!(data instanceof ArrayBuffer)) { console.warn('UNKNOWN data received', data); @@ -184,7 +218,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost onMessage: emitter.event, send: vsbuf => { const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); - worker.postMessage(data, [data]); + port.postMessage(data, [data]); } }; diff --git a/src/vs/workbench/services/extensions/common/webWorkerIframe.ts b/src/vs/workbench/services/extensions/common/webWorkerIframe.ts index bf4731d3909..7b354788d46 100644 --- a/src/vs/workbench/services/extensions/common/webWorkerIframe.ts +++ b/src/vs/workbench/services/extensions/common/webWorkerIframe.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ export const WEB_WORKER_IFRAME = { - sha: 'sha256-rSINb5Ths99Zj4Ml59jEdHS4WbO+H5Iw+oyRmyi2MLw=', + sha: 'sha256-r24mDVsMuFEo8ChaY9ppVJKbY3CUM4I12Aw/yscWZbg=', js: ` (function() { const workerSrc = document.getElementById('vscode-worker-src').getAttribute('data-value'); @@ -13,8 +13,8 @@ export const WEB_WORKER_IFRAME = { worker.onmessage = (event) => { const { data } = event; - if (!(data instanceof ArrayBuffer)) { - console.warn('Unknown data received', data); + if (!(data instanceof MessagePort)) { + console.warn('Unknown data received', event); window.parent.postMessage({ vscodeWebWorkerExtHostId, error: { @@ -42,16 +42,6 @@ export const WEB_WORKER_IFRAME = { } }, '*'); }; - - window.addEventListener('message', function(event) { - if (event.source !== window.parent) { - return; - } - if (event.data.vscodeWebWorkerExtHostId !== vscodeWebWorkerExtHostId) { - return; - } - worker.postMessage(event.data.data, [event.data.data]); - }, false); })(); ` }; diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts index c1e85e6539f..3cffe4e2062 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts @@ -33,7 +33,7 @@ self.close = () => console.trace(`'close' has been blocked`); const nativePostMessage = postMessage.bind(self); self.postMessage = () => console.trace(`'postMessage' has been blocked`); -const nativeAddEventLister = addEventListener.bind(self); +// const nativeAddEventLister = addEventListener.bind(self); self.addEventLister = () => console.trace(`'addEventListener' has been blocked`); (self)['AMDLoader'] = undefined; @@ -79,11 +79,14 @@ class ExtensionWorker { constructor() { - let emitter = new Emitter(); + const channel = new MessageChannel(); + const emitter = new Emitter(); let terminating = false; + // send over port2, keep port1 + nativePostMessage(channel.port2, [channel.port2]); - nativeAddEventLister('message', event => { + channel.port1.onmessage = event => { const { data } = event; if (!(data instanceof ArrayBuffer)) { console.warn('UNKNOWN data received', data); @@ -100,14 +103,14 @@ class ExtensionWorker { // emit non-terminate messages to the outside emitter.fire(msg); - }); + }; this.protocol = { onMessage: emitter.event, send: vsbuf => { if (!terminating) { const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); - nativePostMessage(data, [data]); + channel.port1.postMessage(data, [data]); } } }; diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 2c81c587686..6ac6cdd6c86 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -329,19 +329,6 @@ export class BrowserHostService extends Disposable implements IHostService { //#endregion - - //#region Color Scheme - - // TODO@martin TODO@ben this currently does not support detecting changes - onDidChangeColorScheme = Event.None; - - get colorScheme() { - return this.environmentService.configuration.colorScheme; - } - - //#endregion - - //#region Lifecycle async restart(): Promise { diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts index 8a0d724f07c..25f16cf7b07 100644 --- a/src/vs/workbench/services/host/browser/host.ts +++ b/src/vs/workbench/services/host/browser/host.ts @@ -5,7 +5,7 @@ import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IWindowOpenable, IOpenWindowOptions, IOpenEmptyWindowOptions, ColorScheme } from 'vs/platform/windows/common/windows'; +import { IWindowOpenable, IOpenWindowOptions, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; export const IHostService = createDecorator('hostService'); @@ -65,16 +65,6 @@ export interface IHostService { //#endregion - - //#region Color Scheme - - readonly colorScheme: ColorScheme; - - readonly onDidChangeColorScheme: Event; - - //#endregion - - //#region Lifecycle /** diff --git a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts index 49ed4a1e918..94a0121afd1 100644 --- a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter, Event } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IWindowOpenable, IOpenWindowOptions, isFolderToOpen, isWorkspaceToOpen, IOpenEmptyWindowOptions, ColorScheme } from 'vs/platform/windows/common/windows'; +import { IWindowOpenable, IOpenWindowOptions, isFolderToOpen, isWorkspaceToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { Disposable } from 'vs/base/common/lifecycle'; export class NativeHostService extends Disposable implements IHostService { @@ -22,21 +22,8 @@ export class NativeHostService extends Disposable implements IHostService { @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService ) { super(); - - this.registerListeners(); } - private registerListeners(): void { - - // Color Scheme - this._register(this.electronService.onColorSchemeChange(scheme => { - this._colorScheme = scheme; - - this._onDidChangeColorScheme.fire(); - })); - } - - //#region Focus get onDidChangeFocus(): Event { return this._onDidChangeFocus; } @@ -105,17 +92,6 @@ export class NativeHostService extends Disposable implements IHostService { //#endregion - //#region Color Scheme - - private readonly _onDidChangeColorScheme = this._register(new Emitter()); - readonly onDidChangeColorScheme = this._onDidChangeColorScheme.event; - - private _colorScheme: ColorScheme = this.environmentService.configuration.colorScheme; - get colorScheme() { return this._colorScheme; } - - //#endregion - - //#region Lifecycle focus(options?: { force: boolean }): Promise { diff --git a/src/vs/workbench/services/hover/browser/hover.ts b/src/vs/workbench/services/hover/browser/hover.ts index 9bf40ada030..af6473009e7 100644 --- a/src/vs/workbench/services/hover/browser/hover.ts +++ b/src/vs/workbench/services/hover/browser/hover.ts @@ -30,6 +30,11 @@ export interface IHoverService { * ``` */ showHover(options: IHoverOptions, focus?: boolean): IDisposable | undefined; + + /** + * Hides the hover if it was visible. + */ + hideHover(): void; } export interface IHoverOptions { diff --git a/src/vs/workbench/services/hover/browser/hoverService.ts b/src/vs/workbench/services/hover/browser/hoverService.ts index 30684fc337a..28809ada167 100644 --- a/src/vs/workbench/services/hover/browser/hoverService.ts +++ b/src/vs/workbench/services/hover/browser/hoverService.ts @@ -47,6 +47,14 @@ export class HoverService implements IHoverService { return hover; } + hideHover(): void { + if (!this._currentHoverOptions) { + return; + } + this._currentHoverOptions = undefined; + this._contextViewService.hideContextView(); + } + private _intersectionChange(entries: IntersectionObserverEntry[], hover: IDisposable): void { const entry = entries[entries.length - 1]; if (!entry.isIntersecting) { diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts index f4e8a645899..4d10fd8fecc 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts @@ -117,7 +117,7 @@ suite('KeybindingsEditing', () => { const fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IUriIdentityService, new UriIdentityService(fileService)); instantiationService.stub(IWorkingCopyService, new TestWorkingCopyService()); diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index e6a180a5c3e..7af8b182127 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -7,7 +7,6 @@ import { Emitter } from 'vs/base/common/event'; import { parse } from 'vs/base/common/json'; import { Disposable } from 'vs/base/common/lifecycle'; import * as network from 'vs/base/common/network'; -import { assign } from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; import { getCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition } from 'vs/editor/common/core/position'; @@ -346,7 +345,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic if (!options) { options = { pinned: true }; } else { - options = assign(options, { pinned: true }); + options = { ...options, pinned: true }; } if (openDefaultSettings) { @@ -368,7 +367,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic if (!options) { options = { pinned: true }; } else { - options = assign(options, { pinned: true }); + options = { ...options, pinned: true }; } const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(configurationTarget)); diff --git a/src/vs/workbench/services/themes/browser/browserHostColorSchemeService.ts b/src/vs/workbench/services/themes/browser/browserHostColorSchemeService.ts new file mode 100644 index 00000000000..086043eb041 --- /dev/null +++ b/src/vs/workbench/services/themes/browser/browserHostColorSchemeService.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IHostColorSchemeService } from 'vs/workbench/services/themes/common/hostColorSchemeService'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; + +export class BrowserHostColorSchemeService extends Disposable implements IHostColorSchemeService { + + declare readonly _serviceBrand: undefined; + + private readonly _onDidSchemeChangeEvent = this._register(new Emitter()); + + constructor( + @IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService + ) { + super(); + + this.registerListeners(); + } + + private registerListeners(): void { + + window.matchMedia('(prefers-color-scheme: dark)').addListener(() => { + this._onDidSchemeChangeEvent.fire(); + }); + window.matchMedia('(forced-colors: active)').addListener(() => { + this._onDidSchemeChangeEvent.fire(); + }); + } + + get onDidChangeColorScheme(): Event { + return this._onDidSchemeChangeEvent.event; + } + + get colorScheme(): ColorScheme { + if (window.matchMedia(`(forced-colors: active)`).matches) { + return ColorScheme.HIGH_CONTRAST; + } else if (window.matchMedia(`(prefers-color-scheme: light)`).matches) { + return ColorScheme.LIGHT; + } else if (window.matchMedia(`(prefers-color-scheme: dark)`).matches) { + return ColorScheme.DARK; + } + return this.environmentService.configuration.colorScheme; + } + +} + +registerSingleton(IHostColorSchemeService, BrowserHostColorSchemeService, true); diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index ff15ab584c5..39c18b0c7d3 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -13,7 +13,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import * as errors from 'vs/base/common/errors'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ColorThemeData } from 'vs/workbench/services/themes/common/colorThemeData'; -import { IColorTheme, Extensions as ThemingExtensions, IThemingRegistry, ThemeType, LIGHT, DARK, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { IColorTheme, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService'; import { Event, Emitter } from 'vs/base/common/event'; import { registerFileIconThemeSchemas } from 'vs/workbench/services/themes/common/fileIconThemeSchema'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -34,7 +34,9 @@ import { ProductIconThemeData, DEFAULT_PRODUCT_ICON_THEME_ID } from 'vs/workbenc import { registerProductIconThemeSchemas } from 'vs/workbench/services/themes/common/productIconThemeSchema'; import { ILogService } from 'vs/platform/log/common/log'; import { isWeb } from 'vs/base/common/platform'; -import { ColorScheme } from 'vs/platform/windows/common/windows'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { IHostColorSchemeService } from 'vs/workbench/services/themes/common/hostColorSchemeService'; + // implementation @@ -92,7 +94,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private readonly onProductIconThemeChange: Emitter; private readonly productIconThemeWatcher: ThemeFileWatcher; - private isOSInHighContrast: boolean; // tracking the high contrast state of the OS eventauilly should go out to a seperate service private themeSettingIdBeforeSchemeSwitch: string | undefined; constructor( @@ -104,13 +105,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { @IFileService private readonly fileService: IFileService, @IExtensionResourceLoaderService private readonly extensionResourceLoaderService: IExtensionResourceLoaderService, @IWorkbenchLayoutService readonly layoutService: IWorkbenchLayoutService, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @IHostColorSchemeService private readonly hostColorService: IHostColorSchemeService, ) { this.container = layoutService.container; this.settings = new ThemeConfiguration(configurationService); - this.isOSInHighContrast = environmentService.configuration.colorScheme === ColorScheme.HIGH_CONTRAST; - this.colorThemeRegistry = new ThemeRegistry(extensionService, colorThemesExtPoint, ColorThemeData.fromExtensionTheme); this.colorThemeWatcher = new ThemeFileWatcher(fileService, environmentService, this.reloadCurrentColorTheme.bind(this)); this.onColorThemeChange = new Emitter({ leakWarningThreshold: 400 }); @@ -144,7 +144,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } if (!themeData) { - themeData = ColorThemeData.createUnloadedThemeForThemeType(isWeb ? LIGHT : DARK); + themeData = ColorThemeData.createUnloadedThemeForThemeType(isWeb ? ColorScheme.LIGHT : ColorScheme.DARK); } themeData.setCustomizations(this.settings); this.applyTheme(themeData, undefined, true); @@ -217,14 +217,14 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (e.affectsConfiguration(ThemeSettings.DETECT_COLOR_SCHEME) || e.affectsConfiguration(ThemeSettings.DETECT_HC)) { this.handlePreferredSchemeUpdated(); } - if (e.affectsConfiguration(ThemeSettings.PREFERRED_DARK_THEME) && this.getPreferredColorScheme() === DARK) { - this.applyPreferredColorTheme(DARK); + if (e.affectsConfiguration(ThemeSettings.PREFERRED_DARK_THEME) && this.getPreferredColorScheme() === ColorScheme.DARK) { + this.applyPreferredColorTheme(ColorScheme.DARK); } - if (e.affectsConfiguration(ThemeSettings.PREFERRED_LIGHT_THEME) && this.getPreferredColorScheme() === LIGHT) { - this.applyPreferredColorTheme(LIGHT); + if (e.affectsConfiguration(ThemeSettings.PREFERRED_LIGHT_THEME) && this.getPreferredColorScheme() === ColorScheme.LIGHT) { + this.applyPreferredColorTheme(ColorScheme.LIGHT); } - if (e.affectsConfiguration(ThemeSettings.PREFERRED_HC_THEME) && this.getPreferredColorScheme() === HIGH_CONTRAST) { - this.applyPreferredColorTheme(HIGH_CONTRAST); + if (e.affectsConfiguration(ThemeSettings.PREFERRED_HC_THEME) && this.getPreferredColorScheme() === ColorScheme.HIGH_CONTRAST) { + this.applyPreferredColorTheme(ColorScheme.HIGH_CONTRAST); } if (e.affectsConfiguration(ThemeSettings.FILE_ICON_THEME)) { this.restoreFileIconTheme(); @@ -325,14 +325,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { // preferred scheme handling private installPreferredSchemeListener() { - window.matchMedia('(prefers-color-scheme: dark)').addListener(async () => this.handlePreferredSchemeUpdated()); - } - - public setOSHighContrast(highContrast: boolean): void { - if (this.isOSInHighContrast !== highContrast) { - this.isOSInHighContrast = highContrast; - this.handlePreferredSchemeUpdated(); - } + this.hostColorService.onDidChangeColorScheme(() => this.handlePreferredSchemeUpdated()); } private async handlePreferredSchemeUpdated() { @@ -357,23 +350,22 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return undefined; } - private getPreferredColorScheme(): ThemeType | undefined { + private getPreferredColorScheme(): ColorScheme | undefined { const detectHCThemeSetting = this.configurationService.getValue(ThemeSettings.DETECT_HC); - if (this.isOSInHighContrast && detectHCThemeSetting) { - return HIGH_CONTRAST; + if (this.hostColorService.colorScheme === ColorScheme.HIGH_CONTRAST && detectHCThemeSetting) { + return ColorScheme.HIGH_CONTRAST; } if (this.configurationService.getValue(ThemeSettings.DETECT_COLOR_SCHEME)) { - if (window.matchMedia(`(prefers-color-scheme: light)`).matches) { - return LIGHT; - } else if (window.matchMedia(`(prefers-color-scheme: dark)`).matches) { - return DARK; + const osScheme = this.hostColorService.colorScheme; + if (osScheme !== ColorScheme.HIGH_CONTRAST) { + return osScheme; } } return undefined; } - private async applyPreferredColorTheme(type: ThemeType): Promise { - const settingId = type === DARK ? ThemeSettings.PREFERRED_DARK_THEME : type === LIGHT ? ThemeSettings.PREFERRED_LIGHT_THEME : ThemeSettings.PREFERRED_HC_THEME; + private async applyPreferredColorTheme(type: ColorScheme): Promise { + const settingId = type === ColorScheme.DARK ? ThemeSettings.PREFERRED_DARK_THEME : type === ColorScheme.LIGHT ? ThemeSettings.PREFERRED_LIGHT_THEME : ThemeSettings.PREFERRED_HC_THEME; const themeSettingId = this.configurationService.getValue(settingId); if (themeSettingId) { const theme = await this.colorThemeRegistry.findThemeBySettingsId(themeSettingId, undefined); @@ -446,6 +438,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } }; + ruleCollector.addRule(`.monaco-workbench { forced-color-adjust: none; }`); themingRegistry.getThemingParticipants().forEach(p => p(themeData, ruleCollector, this.environmentService)); _applyRules([...cssRules].join('\n'), colorThemeRulesClassName); } diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index b8c41191476..932b34cefbb 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -14,7 +14,7 @@ import * as objects from 'vs/base/common/objects'; import * as arrays from 'vs/base/common/arrays'; import * as resources from 'vs/base/common/resources'; import { Extensions as ColorRegistryExtensions, IColorRegistry, ColorIdentifier, editorBackground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; -import { ThemeType, ITokenStyle, getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; +import { ITokenStyle, getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; import { Registry } from 'vs/platform/registry/common/platform'; import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; import { URI } from 'vs/base/common/uri'; @@ -26,6 +26,7 @@ import { IExtensionResourceLoaderService } from 'vs/workbench/services/extension import { CharCode } from 'vs/base/common/charCode'; import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; import { ThemeConfiguration } from 'vs/workbench/services/themes/common/themeConfiguration'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; let colorRegistry = Registry.as(ColorRegistryExtensions.ColorContribution); @@ -540,17 +541,17 @@ export class ColorThemeData implements IWorkbenchColorTheme { return this.id.split(' ')[0]; } - get type(): ThemeType { + get type(): ColorScheme { switch (this.baseTheme) { - case VS_LIGHT_THEME: return 'light'; - case VS_HC_THEME: return 'hc'; - default: return 'dark'; + case VS_LIGHT_THEME: return ColorScheme.LIGHT; + case VS_HC_THEME: return ColorScheme.HIGH_CONTRAST; + default: return ColorScheme.DARK; } } // constructors - static createUnloadedThemeForThemeType(themeType: ThemeType, colorMap?: { [id: string]: string }): ColorThemeData { + static createUnloadedThemeForThemeType(themeType: ColorScheme, colorMap?: { [id: string]: string }): ColorThemeData { return ColorThemeData.createUnloadedTheme(getThemeTypeSelector(themeType), colorMap); } diff --git a/src/vs/workbench/services/themes/common/hostColorSchemeService.ts b/src/vs/workbench/services/themes/common/hostColorSchemeService.ts new file mode 100644 index 00000000000..a1fac0124e7 --- /dev/null +++ b/src/vs/workbench/services/themes/common/hostColorSchemeService.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; + +export const IHostColorSchemeService = createDecorator('hostColorSchemeService'); + +export interface IHostColorSchemeService { + + readonly _serviceBrand: undefined; + + readonly colorScheme: ColorScheme; + readonly onDidChangeColorScheme: Event; + +} diff --git a/src/vs/workbench/services/themes/common/themeConfiguration.ts b/src/vs/workbench/services/themes/common/themeConfiguration.ts index cd4ad6203f5..795b03edf96 100644 --- a/src/vs/workbench/services/themes/common/themeConfiguration.ts +++ b/src/vs/workbench/services/themes/common/themeConfiguration.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationNode, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { textmateColorsSchemaId, textmateColorGroupSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; @@ -96,6 +96,13 @@ const productIconThemeSettingSchema: IConfigurationPropertySchema = { errorMessage: nls.localize('productIconThemeError', "Product icon theme is unknown or not installed.") }; +const detectHCSchemeSettingSchema: IConfigurationPropertySchema = { + type: 'boolean', + default: true, + description: nls.localize('autoDetectHighContrast', "If enabled, will automatically change to high contrast theme if the OS is using a high contrast theme."), + scope: ConfigurationScope.APPLICATION +}; + const themeSettingsConfiguration: IConfigurationNode = { id: 'workbench', order: 7.1, @@ -105,7 +112,6 @@ const themeSettingsConfiguration: IConfigurationNode = { [ThemeSettings.PREFERRED_DARK_THEME]: preferredDarkThemeSettingSchema, [ThemeSettings.PREFERRED_LIGHT_THEME]: preferredLightThemeSettingSchema, [ThemeSettings.PREFERRED_HC_THEME]: preferredHCThemeSettingSchema, - [ThemeSettings.DETECT_COLOR_SCHEME]: detectColorSchemeSettingSchema, [ThemeSettings.FILE_ICON_THEME]: fileIconThemeSettingSchema, [ThemeSettings.COLOR_CUSTOMIZATIONS]: colorCustomizationsSchema, [ThemeSettings.PRODUCT_ICON_THEME]: productIconThemeSettingSchema @@ -113,6 +119,17 @@ const themeSettingsConfiguration: IConfigurationNode = { }; configurationRegistry.registerConfiguration(themeSettingsConfiguration); +const themeSettingsWindowConfiguration: IConfigurationNode = { + id: 'window', + order: 8.1, + type: 'object', + properties: { + [ThemeSettings.DETECT_HC]: detectHCSchemeSettingSchema, + [ThemeSettings.DETECT_COLOR_SCHEME]: detectColorSchemeSettingSchema, + } +}; +configurationRegistry.registerConfiguration(themeSettingsWindowConfiguration); + function tokenGroupSettings(description: string): IJSONSchema { return { description, diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index cf14976f6fa..9186681075f 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -77,8 +77,6 @@ export interface IWorkbenchThemeService extends IThemeService { getProductIconTheme(): IWorkbenchProductIconTheme; getProductIconThemes(): Promise; onDidProductIconThemeChange: Event; - - setOSHighContrast(highContrast: boolean): void; } export interface IColorCustomizations { diff --git a/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts b/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts new file mode 100644 index 00000000000..988e308f228 --- /dev/null +++ b/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { IHostColorSchemeService } from 'vs/workbench/services/themes/common/hostColorSchemeService'; + +export class NativeHostColorSchemeService extends Disposable implements IHostColorSchemeService { + + declare readonly _serviceBrand: undefined; + + constructor( + @IElectronService private readonly electronService: IElectronService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService + ) { + super(); + + this.registerListeners(); + } + + private registerListeners(): void { + + // Color Scheme + this._register(this.electronService.onColorSchemeChange(scheme => { + this._colorScheme = scheme; + + this._onDidChangeColorScheme.fire(); + })); + } + + private readonly _onDidChangeColorScheme = this._register(new Emitter()); + readonly onDidChangeColorScheme = this._onDidChangeColorScheme.event; + + private _colorScheme: ColorScheme = this.environmentService.configuration.colorScheme; + get colorScheme() { return this._colorScheme; } + +} + +registerSingleton(IHostColorSchemeService, NativeHostColorSchemeService, true); diff --git a/src/vs/workbench/services/userData/common/fileUserDataProvider.ts b/src/vs/workbench/services/userData/common/fileUserDataProvider.ts index 3289ab788b6..91158913e25 100644 --- a/src/vs/workbench/services/userData/common/fileUserDataProvider.ts +++ b/src/vs/workbench/services/userData/common/fileUserDataProvider.ts @@ -7,7 +7,6 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileWriteOptions, FileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithOpenReadWriteCloseCapability, FileOpenOptions, hasReadWriteCapability, hasOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadStreamCapability, FileReadStreamOptions, hasFileReadStreamCapability } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; -import { BACKUPS } from 'vs/platform/environment/common/environment'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ReadableStreamEvents } from 'vs/base/common/stream'; @@ -26,11 +25,12 @@ export class FileUserDataProvider extends Disposable implements readonly onDidChangeFile: Event = this._onDidChangeFile.event; private readonly userDataHome: URI; + private readonly backupHome: URI | undefined; private extUri: ExtUri; constructor( private readonly fileSystemUserDataHome: URI, - private readonly fileSystemBackupsHome: URI, + private readonly fileSystemBackupsHome: URI | undefined, private readonly fileSystemProvider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, environmentService: IWorkbenchEnvironmentService, private readonly logService: ILogService, @@ -38,6 +38,7 @@ export class FileUserDataProvider extends Disposable implements super(); this.userDataHome = environmentService.userRoamingDataHome; + this.backupHome = environmentService.backupWorkspaceHome; this.extUri = !!(this.capabilities & FileSystemProviderCapabilities.PathCaseSensitive) ? extUri : extUriIgnorePathCase; // update extUri as capabilites might change. @@ -139,10 +140,13 @@ export class FileUserDataProvider extends Disposable implements } private toFileSystemResource(userDataResource: URI): URI { - const relativePath = this.extUri.relativePath(this.userDataHome, userDataResource)!; - if (relativePath.startsWith(BACKUPS)) { - return this.extUri.joinPath(this.extUri.dirname(this.fileSystemBackupsHome), relativePath); + // Backup Resource + if (this.backupHome && this.fileSystemBackupsHome && this.extUri.isEqualOrParent(userDataResource, this.backupHome)) { + const relativePath = this.extUri.relativePath(this.backupHome, userDataResource); + return relativePath ? this.extUri.joinPath(this.fileSystemBackupsHome, relativePath) : this.fileSystemBackupsHome; } + + const relativePath = this.extUri.relativePath(this.userDataHome, userDataResource)!; return this.extUri.joinPath(this.fileSystemUserDataHome, relativePath); } @@ -151,9 +155,9 @@ export class FileUserDataProvider extends Disposable implements const relativePath = this.extUri.relativePath(this.fileSystemUserDataHome, fileSystemResource); return relativePath ? this.extUri.joinPath(this.userDataHome, relativePath) : this.userDataHome; } - if (this.extUri.isEqualOrParent(fileSystemResource, this.fileSystemBackupsHome)) { + if (this.backupHome && this.fileSystemBackupsHome && this.extUri.isEqualOrParent(fileSystemResource, this.fileSystemBackupsHome)) { const relativePath = this.extUri.relativePath(this.fileSystemBackupsHome, fileSystemResource); - return relativePath ? this.extUri.joinPath(this.userDataHome, BACKUPS, relativePath) : this.extUri.joinPath(this.userDataHome, BACKUPS); + return relativePath ? this.extUri.joinPath(this.backupHome, relativePath) : this.backupHome; } return null; } diff --git a/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts b/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts index 7f01f78c3eb..999fade0edd 100644 --- a/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts +++ b/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts @@ -7,7 +7,6 @@ import * as assert from 'assert'; import * as os from 'os'; import * as path from 'vs/base/common/path'; import * as uuid from 'vs/base/common/uuid'; -import * as pfs from 'vs/base/node/pfs'; import { IFileService, FileChangeType, IFileChange, IFileSystemProviderWithFileReadWriteCapability, IStat, FileType, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -17,29 +16,21 @@ import { FileUserDataProvider } from 'vs/workbench/services/userData/common/file import { joinPath, dirname } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; -import { BACKUPS } from 'vs/platform/environment/common/environment'; import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { Emitter, Event } from 'vs/base/common/event'; import { timeout } from 'vs/base/common/async'; - -class TestBrowserWorkbenchEnvironmentService extends BrowserWorkbenchEnvironmentService { - - testUserRoamingDataHome!: URI; - - get userRoamingDataHome(): URI { - return this.testUserRoamingDataHome; - } -} +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; suite('FileUserDataProvider', () => { let testObject: IFileService; - let rootPath: string; - let userDataPath: string; - let backupsPath: string; - let userDataResource: URI; + let rootResource: URI; + let userDataHomeOnDisk: URI; + let backupWorkspaceHomeOnDisk: URI; + let environmentService: IWorkbenchEnvironmentService; const disposables = new DisposableStore(); + let fileUserDataProvider: FileUserDataProvider; setup(async () => { const logService = new NullLogService(); @@ -50,237 +41,238 @@ suite('FileUserDataProvider', () => { disposables.add(diskFileSystemProvider); disposables.add(testObject.registerProvider(Schemas.file, diskFileSystemProvider)); - rootPath = path.join(os.tmpdir(), 'vsctests', uuid.generateUuid()); - userDataPath = path.join(rootPath, 'user'); - backupsPath = path.join(rootPath, BACKUPS); - userDataResource = URI.file(userDataPath).with({ scheme: Schemas.userData }); - await Promise.all([pfs.mkdirp(userDataPath), pfs.mkdirp(backupsPath)]); + const workspaceId = 'workspaceId'; + environmentService = new BrowserWorkbenchEnvironmentService({ remoteAuthority: 'remote', workspaceId, logsPath: URI.file('logFile') }); - const environmentService = new TestBrowserWorkbenchEnvironmentService({ remoteAuthority: 'remote', workspaceId: 'workspaceId', logsPath: URI.file('logFile') }); - environmentService.testUserRoamingDataHome = userDataResource; + rootResource = URI.file(path.join(os.tmpdir(), 'vsctests', uuid.generateUuid())); + userDataHomeOnDisk = joinPath(rootResource, 'user'); + const backupHome = joinPath(rootResource, 'Backups'); + backupWorkspaceHomeOnDisk = joinPath(backupHome, workspaceId); + await Promise.all([testObject.createFolder(userDataHomeOnDisk), testObject.createFolder(backupWorkspaceHomeOnDisk)]); - const userDataFileSystemProvider = new FileUserDataProvider(URI.file(userDataPath), URI.file(backupsPath), diskFileSystemProvider, environmentService, logService); - disposables.add(userDataFileSystemProvider); - disposables.add(testObject.registerProvider(Schemas.userData, userDataFileSystemProvider)); + fileUserDataProvider = new FileUserDataProvider(userDataHomeOnDisk, backupWorkspaceHomeOnDisk, diskFileSystemProvider, environmentService, logService); + disposables.add(fileUserDataProvider); + disposables.add(testObject.registerProvider(Schemas.userData, fileUserDataProvider)); }); teardown(async () => { + fileUserDataProvider.dispose(); // need to dispose first, otherwise del will fail (https://github.com/microsoft/vscode/issues/106283) + await testObject.del(rootResource, { recursive: true }); disposables.clear(); - await pfs.rimraf(rootPath, pfs.RimRafMode.MOVE); }); test('exists return false when file does not exist', async () => { - const exists = await testObject.exists(joinPath(userDataResource, 'settings.json')); + const exists = await testObject.exists(environmentService.settingsResource); assert.equal(exists, false); }); test('read file throws error if not exist', async () => { try { - await testObject.readFile(joinPath(userDataResource, 'settings.json')); + await testObject.readFile(environmentService.settingsResource); assert.fail('Should fail since file does not exist'); } catch (e) { } }); test('read existing file', async () => { - await pfs.writeFile(path.join(userDataPath, 'settings.json'), '{}'); - const result = await testObject.readFile(joinPath(userDataResource, 'settings.json')); + await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('{}')); + const result = await testObject.readFile(environmentService.settingsResource); assert.equal(result.value, '{}'); }); test('create file', async () => { - const resource = joinPath(userDataResource, 'settings.json'); + const resource = environmentService.settingsResource; const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.equal(actual1.resource.toString(), resource.toString()); - const actual2 = await pfs.readFile(path.join(userDataPath, 'settings.json')); - assert.equal(actual2, '{}'); + const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'settings.json')); + assert.equal(actual2.value.toString(), '{}'); }); test('write file creates the file if not exist', async () => { - const resource = joinPath(userDataResource, 'settings.json'); + const resource = environmentService.settingsResource; const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.equal(actual1.resource.toString(), resource.toString()); - const actual2 = await pfs.readFile(path.join(userDataPath, 'settings.json')); - assert.equal(actual2, '{}'); + const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'settings.json')); + assert.equal(actual2.value.toString(), '{}'); }); test('write to existing file', async () => { - const resource = joinPath(userDataResource, 'settings.json'); - await pfs.writeFile(path.join(userDataPath, 'settings.json'), '{}'); + const resource = environmentService.settingsResource; + await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('{}')); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}')); assert.equal(actual1.resource.toString(), resource.toString()); - const actual2 = await pfs.readFile(path.join(userDataPath, 'settings.json')); - assert.equal(actual2, '{a:1}'); + const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'settings.json')); + assert.equal(actual2.value.toString(), '{a:1}'); }); test('delete file', async () => { - await pfs.writeFile(path.join(userDataPath, 'settings.json'), ''); - await testObject.del(joinPath(userDataResource, 'settings.json')); - const result = await pfs.exists(path.join(userDataPath, 'settings.json')); + await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('')); + await testObject.del(environmentService.settingsResource); + const result = await testObject.exists(joinPath(userDataHomeOnDisk, 'settings.json')); assert.equal(false, result); }); test('resolve file', async () => { - await pfs.writeFile(path.join(userDataPath, 'settings.json'), ''); - const result = await testObject.resolve(joinPath(userDataResource, 'settings.json')); + await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('')); + const result = await testObject.resolve(environmentService.settingsResource); assert.ok(!result.isDirectory); assert.ok(result.children === undefined); }); test('exists return false for folder that does not exist', async () => { - const exists = await testObject.exists(joinPath(userDataResource, 'snippets')); + const exists = await testObject.exists(environmentService.snippetsHome); assert.equal(exists, false); }); test('exists return true for folder that exists', async () => { - await pfs.mkdirp(path.join(userDataPath, 'snippets')); - const exists = await testObject.exists(joinPath(userDataResource, 'snippets')); + await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); + const exists = await testObject.exists(environmentService.snippetsHome); assert.equal(exists, true); }); test('read file throws error for folder', async () => { - await pfs.mkdirp(path.join(userDataPath, 'snippets')); + await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); try { - await testObject.readFile(joinPath(userDataResource, 'snippets')); + await testObject.readFile(environmentService.snippetsHome); assert.fail('Should fail since read file is not supported for folders'); } catch (e) { } }); test('read file under folder', async () => { - await pfs.mkdirp(path.join(userDataPath, 'snippets')); - await pfs.writeFile(path.join(userDataPath, 'snippets', 'settings.json'), '{}'); - const resource = joinPath(userDataResource, 'snippets/settings.json'); + await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); + await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); + const resource = joinPath(environmentService.snippetsHome, 'settings.json'); const actual = await testObject.readFile(resource); assert.equal(actual.resource.toString(), resource.toString()); assert.equal(actual.value, '{}'); }); test('read file under sub folder', async () => { - await pfs.mkdirp(path.join(userDataPath, 'snippets', 'java')); - await pfs.writeFile(path.join(userDataPath, 'snippets', 'java', 'settings.json'), '{}'); - const resource = joinPath(userDataResource, 'snippets/java/settings.json'); + await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets', 'java')); + await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'java', 'settings.json'), VSBuffer.fromString('{}')); + const resource = joinPath(environmentService.snippetsHome, 'java/settings.json'); const actual = await testObject.readFile(resource); assert.equal(actual.resource.toString(), resource.toString()); assert.equal(actual.value, '{}'); }); test('create file under folder that exists', async () => { - await pfs.mkdirp(path.join(userDataPath, 'snippets')); - const resource = joinPath(userDataResource, 'snippets/settings.json'); + await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); + const resource = joinPath(environmentService.snippetsHome, 'settings.json'); const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.equal(actual1.resource.toString(), resource.toString()); - const actual2 = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json')); - assert.equal(actual2, '{}'); + const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); + assert.equal(actual2.value.toString(), '{}'); }); test('create file under folder that does not exist', async () => { - const resource = joinPath(userDataResource, 'snippets/settings.json'); + const resource = joinPath(environmentService.snippetsHome, 'settings.json'); const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.equal(actual1.resource.toString(), resource.toString()); - const actual2 = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json')); - assert.equal(actual2, '{}'); + const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); + assert.equal(actual2.value.toString(), '{}'); }); test('write to not existing file under container that exists', async () => { - await pfs.mkdirp(path.join(userDataPath, 'snippets')); - const resource = joinPath(userDataResource, 'snippets/settings.json'); + await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); + const resource = joinPath(environmentService.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.equal(actual1.resource.toString(), resource.toString()); - const actual = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json')); - assert.equal(actual, '{}'); + const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); + assert.equal(actual.value.toString(), '{}'); }); test('write to not existing file under container that does not exists', async () => { - const resource = joinPath(userDataResource, 'snippets/settings.json'); + const resource = joinPath(environmentService.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.equal(actual1.resource.toString(), resource.toString()); - const actual = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json')); - assert.equal(actual, '{}'); + const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); + assert.equal(actual.value.toString(), '{}'); }); test('write to existing file under container', async () => { - await pfs.mkdirp(path.join(userDataPath, 'snippets')); - await pfs.writeFile(path.join(userDataPath, 'snippets', 'settings.json'), '{}'); - const resource = joinPath(userDataResource, 'snippets/settings.json'); + await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); + await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); + const resource = joinPath(environmentService.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}')); assert.equal(actual1.resource.toString(), resource.toString()); - const actual = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json')); - assert.equal(actual.toString(), '{a:1}'); + const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); + assert.equal(actual.value.toString(), '{a:1}'); }); test('write file under sub container', async () => { - const resource = joinPath(userDataResource, 'snippets/java/settings.json'); + const resource = joinPath(environmentService.snippetsHome, 'java/settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.equal(actual1.resource.toString(), resource.toString()); - const actual = await pfs.readFile(path.join(userDataPath, 'snippets', 'java', 'settings.json')); - assert.equal(actual, '{}'); + const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'java', 'settings.json')); + assert.equal(actual.value.toString(), '{}'); }); test('delete throws error for folder that does not exist', async () => { try { - await testObject.del(joinPath(userDataResource, 'snippets')); + await testObject.del(environmentService.snippetsHome); assert.fail('Should fail the folder does not exist'); } catch (e) { } }); test('delete not existing file under container that exists', async () => { - await pfs.mkdirp(path.join(userDataPath, 'snippets')); + await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); try { - await testObject.del(joinPath(userDataResource, 'snippets/settings.json')); + await testObject.del(joinPath(environmentService.snippetsHome, 'settings.json')); assert.fail('Should fail since file does not exist'); } catch (e) { } }); test('delete not existing file under container that does not exists', async () => { try { - await testObject.del(joinPath(userDataResource, 'snippets/settings.json')); + await testObject.del(joinPath(environmentService.snippetsHome, 'settings.json')); assert.fail('Should fail since file does not exist'); } catch (e) { } }); test('delete existing file under folder', async () => { - await pfs.mkdirp(path.join(userDataPath, 'snippets')); - await pfs.writeFile(path.join(userDataPath, 'snippets', 'settings.json'), '{}'); - await testObject.del(joinPath(userDataResource, 'snippets/settings.json')); - const exists = await pfs.exists(path.join(userDataPath, 'snippets', 'settings.json')); + await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); + await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); + await testObject.del(joinPath(environmentService.snippetsHome, 'settings.json')); + const exists = await testObject.exists(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); assert.equal(exists, false); }); test('resolve folder', async () => { - await pfs.mkdirp(path.join(userDataPath, 'snippets')); - await pfs.writeFile(path.join(userDataPath, 'snippets', 'settings.json'), '{}'); - const result = await testObject.resolve(joinPath(userDataResource, 'snippets')); + await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); + await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); + const result = await testObject.resolve(environmentService.snippetsHome); assert.ok(result.isDirectory); assert.ok(result.children !== undefined); assert.equal(result.children!.length, 1); - assert.equal(result.children![0].resource.toString(), joinPath(userDataResource, 'snippets/settings.json').toString()); + assert.equal(result.children![0].resource.toString(), joinPath(environmentService.snippetsHome, 'settings.json').toString()); }); test('read backup file', async () => { - await pfs.writeFile(path.join(backupsPath, 'backup.json'), '{}'); - const result = await testObject.readFile(joinPath(userDataResource, `${BACKUPS}/backup.json`)); + await testObject.writeFile(joinPath(backupWorkspaceHomeOnDisk, 'backup.json'), VSBuffer.fromString('{}')); + const result = await testObject.readFile(joinPath(environmentService.backupWorkspaceHome!, `backup.json`)); assert.equal(result.value, '{}'); }); test('create backup file', async () => { - await testObject.createFile(joinPath(userDataResource, `${BACKUPS}/backup.json`), VSBuffer.fromString('{}')); - const result = await pfs.readFile(path.join(backupsPath, 'backup.json')); - assert.equal(result, '{}'); + await testObject.createFile(joinPath(environmentService.backupWorkspaceHome!, `backup.json`), VSBuffer.fromString('{}')); + const result = await testObject.readFile(joinPath(backupWorkspaceHomeOnDisk, 'backup.json')); + assert.equal(result.value.toString(), '{}'); }); test('write backup file', async () => { - await pfs.writeFile(path.join(backupsPath, 'backup.json'), '{}'); - await testObject.writeFile(joinPath(userDataResource, `${BACKUPS}/backup.json`), VSBuffer.fromString('{a:1}')); - const result = await pfs.readFile(path.join(backupsPath, 'backup.json')); - assert.equal(result, '{a:1}'); + await testObject.writeFile(joinPath(backupWorkspaceHomeOnDisk, 'backup.json'), VSBuffer.fromString('{}')); + await testObject.writeFile(joinPath(environmentService.backupWorkspaceHome!, `backup.json`), VSBuffer.fromString('{a:1}')); + const result = await testObject.readFile(joinPath(backupWorkspaceHomeOnDisk, 'backup.json')); + assert.equal(result.value.toString(), '{a:1}'); }); test('resolve backups folder', async () => { - await pfs.writeFile(path.join(backupsPath, 'backup.json'), '{}'); - const result = await testObject.resolve(joinPath(userDataResource, BACKUPS)); + await testObject.writeFile(joinPath(backupWorkspaceHomeOnDisk, 'backup.json'), VSBuffer.fromString('{}')); + const result = await testObject.resolve(environmentService.backupWorkspaceHome!); assert.ok(result.isDirectory); assert.ok(result.children !== undefined); assert.equal(result.children!.length, 1); - assert.equal(result.children![0].resource.toString(), joinPath(userDataResource, `${BACKUPS}/backup.json`).toString()); + assert.equal(result.children![0].resource.toString(), joinPath(environmentService.backupWorkspaceHome!, `backup.json`).toString()); }); }); @@ -315,7 +307,7 @@ suite('FileUserDataProvider - Watching', () => { let testObject: IFileService; let localBackupsResource: URI; let localUserDataResource: URI; - let userDataResource: URI; + let environmentService: IWorkbenchEnvironmentService; const disposables = new DisposableStore(); const fileEventEmitter: Emitter = new Emitter(); @@ -323,15 +315,11 @@ suite('FileUserDataProvider - Watching', () => { setup(() => { - const rootPath = path.join(os.tmpdir(), 'vsctests', uuid.generateUuid()); - const userDataPath = path.join(rootPath, 'user'); - const backupsPath = path.join(rootPath, BACKUPS); - localBackupsResource = URI.file(backupsPath); - localUserDataResource = URI.file(userDataPath); - userDataResource = localUserDataResource.with({ scheme: Schemas.userData }); + environmentService = new BrowserWorkbenchEnvironmentService({ remoteAuthority: 'remote', workspaceId: 'workspaceId', logsPath: URI.file('logFile') }); - const environmentService = new TestBrowserWorkbenchEnvironmentService({ remoteAuthority: 'remote', workspaceId: 'workspaceId', logsPath: URI.file('logFile') }); - environmentService.testUserRoamingDataHome = userDataResource; + const rootResource = URI.file(path.join(os.tmpdir(), 'vsctests', uuid.generateUuid())); + localUserDataResource = joinPath(rootResource, 'user'); + localBackupsResource = joinPath(rootResource, 'Backups'); const userDataFileSystemProvider = new FileUserDataProvider(localUserDataResource, localBackupsResource, new TestFileSystemProvider(fileEventEmitter.event), environmentService, new NullLogService()); disposables.add(userDataFileSystemProvider); @@ -341,12 +329,10 @@ suite('FileUserDataProvider - Watching', () => { disposables.add(testObject.registerProvider(Schemas.userData, userDataFileSystemProvider)); }); - teardown(() => { - disposables.clear(); - }); + teardown(() => disposables.clear()); test('file added change event', done => { - const expected = joinPath(userDataResource, 'settings.json'); + const expected = environmentService.settingsResource; const target = joinPath(localUserDataResource, 'settings.json'); testObject.onDidFilesChange(e => { if (e.contains(expected, FileChangeType.ADDED)) { @@ -360,7 +346,7 @@ suite('FileUserDataProvider - Watching', () => { }); test('file updated change event', done => { - const expected = joinPath(userDataResource, 'settings.json'); + const expected = environmentService.settingsResource; const target = joinPath(localUserDataResource, 'settings.json'); testObject.onDidFilesChange(e => { if (e.contains(expected, FileChangeType.UPDATED)) { @@ -374,7 +360,7 @@ suite('FileUserDataProvider - Watching', () => { }); test('file deleted change event', done => { - const expected = joinPath(userDataResource, 'settings.json'); + const expected = environmentService.settingsResource; const target = joinPath(localUserDataResource, 'settings.json'); testObject.onDidFilesChange(e => { if (e.contains(expected, FileChangeType.DELETED)) { @@ -388,7 +374,7 @@ suite('FileUserDataProvider - Watching', () => { }); test('file under folder created change event', done => { - const expected = joinPath(userDataResource, 'snippets', 'settings.json'); + const expected = joinPath(environmentService.snippetsHome, 'settings.json'); const target = joinPath(localUserDataResource, 'snippets', 'settings.json'); testObject.onDidFilesChange(e => { if (e.contains(expected, FileChangeType.ADDED)) { @@ -402,7 +388,7 @@ suite('FileUserDataProvider - Watching', () => { }); test('file under folder updated change event', done => { - const expected = joinPath(userDataResource, 'snippets', 'settings.json'); + const expected = joinPath(environmentService.snippetsHome, 'settings.json'); const target = joinPath(localUserDataResource, 'snippets', 'settings.json'); testObject.onDidFilesChange(e => { if (e.contains(expected, FileChangeType.UPDATED)) { @@ -416,7 +402,7 @@ suite('FileUserDataProvider - Watching', () => { }); test('file under folder deleted change event', done => { - const expected = joinPath(userDataResource, 'snippets', 'settings.json'); + const expected = joinPath(environmentService.snippetsHome, 'settings.json'); const target = joinPath(localUserDataResource, 'snippets', 'settings.json'); testObject.onDidFilesChange(e => { if (e.contains(expected, FileChangeType.DELETED)) { @@ -444,7 +430,7 @@ suite('FileUserDataProvider - Watching', () => { }); test('backup file created change event', done => { - const expected = joinPath(userDataResource, BACKUPS, 'settings.json'); + const expected = joinPath(environmentService.backupWorkspaceHome!, 'settings.json'); const target = joinPath(localBackupsResource, 'settings.json'); testObject.onDidFilesChange(e => { if (e.contains(expected, FileChangeType.ADDED)) { @@ -458,7 +444,7 @@ suite('FileUserDataProvider - Watching', () => { }); test('backup file update change event', done => { - const expected = joinPath(userDataResource, BACKUPS, 'settings.json'); + const expected = joinPath(environmentService.backupWorkspaceHome!, 'settings.json'); const target = joinPath(localBackupsResource, 'settings.json'); testObject.onDidFilesChange(e => { if (e.contains(expected, FileChangeType.UPDATED)) { @@ -472,7 +458,7 @@ suite('FileUserDataProvider - Watching', () => { }); test('backup file delete change event', done => { - const expected = joinPath(userDataResource, BACKUPS, 'settings.json'); + const expected = joinPath(environmentService.backupWorkspaceHome!, 'settings.json'); const target = joinPath(localBackupsResource, 'settings.json'); testObject.onDidFilesChange(e => { if (e.contains(expected, FileChangeType.DELETED)) { diff --git a/src/vs/workbench/services/views/common/viewContainerModel.ts b/src/vs/workbench/services/views/common/viewContainerModel.ts index a8e7d7ccee7..a360b073c53 100644 --- a/src/vs/workbench/services/views/common/viewContainerModel.ts +++ b/src/vs/workbench/services/views/common/viewContainerModel.ts @@ -12,7 +12,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; -import { firstIndex, move } from 'vs/base/common/arrays'; +import { move } from 'vs/base/common/arrays'; import { isUndefined, isUndefinedOrNull } from 'vs/base/common/types'; import { isEqual } from 'vs/base/common/resources'; @@ -442,8 +442,8 @@ export class ViewContainerModel extends Disposable implements IViewContainerMode } move(from: string, to: string): void { - const fromIndex = firstIndex(this.viewDescriptorItems, v => v.viewDescriptor.id === from); - const toIndex = firstIndex(this.viewDescriptorItems, v => v.viewDescriptor.id === to); + const fromIndex = this.viewDescriptorItems.findIndex(v => v.viewDescriptor.id === from); + const toIndex = this.viewDescriptorItems.findIndex(v => v.viewDescriptor.id === to); const fromViewDescriptor = this.viewDescriptorItems[fromIndex]; const toViewDescriptor = this.viewDescriptorItems[toIndex]; @@ -531,7 +531,7 @@ export class ViewContainerModel extends Disposable implements IViewContainerMode this.contextKeys.delete(key); } } - const index = firstIndex(this.viewDescriptorItems, i => i.viewDescriptor.id === viewDescriptor.id); + const index = this.viewDescriptorItems.findIndex(i => i.viewDescriptor.id === viewDescriptor.id); if (index !== -1) { removed.push(viewDescriptor); const viewDescriptorItem = this.viewDescriptorItems[index]; diff --git a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts index 61b87ae61ef..a03c1809c8d 100644 --- a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts @@ -13,7 +13,6 @@ import { WorkspaceService } from 'vs/workbench/services/configuration/browser/co import { IStorageService } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { toBackupWorkspaceResource } from 'vs/workbench/services/backup/electron-sandbox/backup'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { basename } from 'vs/base/common/resources'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -173,7 +172,6 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi // Reinitialize backup service this.environmentService.configuration.backupPath = result.backupPath; - this.environmentService.configuration.backupWorkspaceResource = result.backupPath ? toBackupWorkspaceResource(result.backupPath, this.environmentService) : undefined; if (this.backupFileService instanceof BackupFileService) { this.backupFileService.reinitialize(); } diff --git a/src/vs/workbench/test/browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/browser/api/extHostConfiguration.test.ts index a442a594c50..470a1db5e6d 100644 --- a/src/vs/workbench/test/browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/browser/api/extHostConfiguration.test.ts @@ -14,7 +14,6 @@ import { mock } from 'vs/base/test/common/mock'; import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ConfigurationTarget, IConfigurationModel, IConfigurationChange } from 'vs/platform/configuration/common/configuration'; import { NullLogService } from 'vs/platform/log/common/log'; -import { assign } from 'vs/base/common/objects'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; suite('ExtHostConfiguration', function () { @@ -211,20 +210,22 @@ suite('ExtHostConfiguration', function () { }), JSON.stringify(actual)); actual = all.getConfiguration('workbench').get('emptyobjectkey'); - actual = assign(actual || {}, { + actual = { + ...(actual || {}), 'statusBar.background': `#0ff`, 'statusBar.foreground': `#ff0`, - }); + }; assert.deepEqual(JSON.stringify({ 'statusBar.background': `#0ff`, 'statusBar.foreground': `#ff0`, }), JSON.stringify(actual)); actual = all.getConfiguration('workbench').get('unknownkey'); - actual = assign(actual || {}, { + actual = { + ...(actual || {}), 'statusBar.background': `#0ff`, 'statusBar.foreground': `#ff0`, - }); + }; assert.deepEqual(JSON.stringify({ 'statusBar.background': `#0ff`, 'statusBar.foreground': `#ff0`, diff --git a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts index f6860e3f9c6..df33e67802a 100644 --- a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts @@ -22,7 +22,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { getDocumentSymbols } from 'vs/editor/contrib/gotoSymbol/documentSymbols'; import * as modes from 'vs/editor/common/modes'; -import { getCodeLensData } from 'vs/editor/contrib/codelens/codelens'; +import { getCodeLensModel } from 'vs/editor/contrib/codelens/codelens'; import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition, getDeclarationsAtPosition, getReferencesAtPosition } from 'vs/editor/contrib/gotoSymbol/goToSymbol'; import { getHover } from 'vs/editor/contrib/hover/getHover'; import { getOccurrencesAtPosition } from 'vs/editor/contrib/wordHighlighter/wordHighlighter'; @@ -189,7 +189,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const value = await getCodeLensData(model, CancellationToken.None); + const value = await getCodeLensModel(model, CancellationToken.None); assert.equal(value.lenses.length, 1); }); @@ -207,7 +207,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const value = await getCodeLensData(model, CancellationToken.None); + const value = await getCodeLensModel(model, CancellationToken.None); assert.equal(value.lenses.length, 1); const [data] = value.lenses; const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); @@ -224,7 +224,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const value = await getCodeLensData(model, CancellationToken.None); + const value = await getCodeLensModel(model, CancellationToken.None); assert.equal(value.lenses.length, 1); let [data] = value.lenses; const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index a4de2b11a68..378c3b194e5 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -32,7 +32,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IInstantiationService, ServicesAccessor, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { MenuBarVisibility, IWindowOpenable, IOpenWindowOptions, IOpenEmptyWindowOptions, ColorScheme } from 'vs/platform/windows/common/windows'; +import { MenuBarVisibility, IWindowOpenable, IOpenWindowOptions, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -114,6 +114,7 @@ import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFil import { newWriteableStream, ReadableStreamEvents } from 'vs/base/common/stream'; import { EncodingOracle, IEncodingOverride } from 'vs/workbench/services/textfile/browser/textFileService'; import { UTF16le, UTF16be, UTF8_with_bom } from 'vs/workbench/services/textfile/common/encoding'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined); @@ -1038,7 +1039,7 @@ export class TestHostService implements IHostService { async toggleFullScreen(): Promise { } - readonly colorScheme = ColorScheme.DEFAULT; + readonly colorScheme = ColorScheme.DARK; onDidChangeColorScheme = Event.None; } diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index c536d51d6f1..decfe69a8c8 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -25,7 +25,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { URI } from 'vs/base/common/uri'; import { IReadTextFileOptions, ITextFileStreamContent, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; -import { IOpenEmptyWindowOptions, IWindowOpenable, IOpenWindowOptions, IOpenedWindow, ColorScheme } from 'vs/platform/windows/common/windows'; +import { IOpenEmptyWindowOptions, IWindowOpenable, IOpenWindowOptions, IOpenedWindow } from 'vs/platform/windows/common/windows'; import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { LogLevel, ILogService } from 'vs/platform/log/common/log'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; @@ -41,6 +41,7 @@ import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/ur import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IOSProperties } from 'vs/platform/electron/common/electron'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; export const TestWorkbenchConfiguration: INativeWorkbenchConfiguration = { windowId: 0, @@ -53,7 +54,7 @@ export const TestWorkbenchConfiguration: INativeWorkbenchConfiguration = { userEnv: {}, execPath: process.execPath, perfEntries: [], - colorScheme: ColorScheme.DEFAULT, + colorScheme: ColorScheme.DARK, ...parseArgs(process.argv, OPTIONS) }; diff --git a/src/vs/workbench/workbench.sandbox.main.ts b/src/vs/workbench/workbench.sandbox.main.ts index d0ec888aac6..3f5a3c61f87 100644 --- a/src/vs/workbench/workbench.sandbox.main.ts +++ b/src/vs/workbench/workbench.sandbox.main.ts @@ -38,6 +38,7 @@ import 'vs/workbench/services/workspaces/electron-sandbox/workspaceEditingServic import 'vs/workbench/services/configurationResolver/electron-sandbox/configurationResolverService'; import 'vs/workbench/services/accessibility/electron-sandbox/accessibilityService'; import 'vs/workbench/services/path/electron-sandbox/pathService'; +import 'vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService'; //#endregion diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 008a5d9e46a..4fe99db6b18 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -147,12 +147,19 @@ interface IWindowIndicator { command?: string; } +enum ColorScheme { + DARK = 'dark', + LIGHT = 'light', + HIGH_CONTRAST = 'hc' +} + + interface IInitialColorTheme { /** * Initial color theme type. */ - themeType: 'light' | 'dark' | 'hc'; + themeType: ColorScheme; /** * A list of workbench colors to apply initially. diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index f02bbbf874b..099c57315cc 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -50,6 +50,7 @@ import 'vs/workbench/services/lifecycle/browser/lifecycleService'; import 'vs/workbench/services/clipboard/browser/clipboardService'; import 'vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService'; import 'vs/workbench/services/path/browser/pathService'; +import 'vs/workbench/services/themes/browser/browserHostColorSchemeService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; diff --git a/test/smoke/src/areas/notebook/notebook.test.ts b/test/smoke/src/areas/notebook/notebook.test.ts index 4e946ffcbe1..4ceb37250f3 100644 --- a/test/smoke/src/areas/notebook/notebook.test.ts +++ b/test/smoke/src/areas/notebook/notebook.test.ts @@ -63,7 +63,7 @@ export function setup() { await app.workbench.notebook.waitForActiveCellEditorContents('code()'); }); - it.skip('cell action execution', async function () { + it('cell action execution', async function () { const app = this.app as Application; await app.workbench.notebook.openNotebook(); await app.workbench.notebook.insertNotebookCell('code'); diff --git a/yarn.lock b/yarn.lock index b3a08230717..774e959c57b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2749,10 +2749,10 @@ electron-to-chromium@^1.2.7: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz#78ecb8a399066187bb374eede35d9c70565a803d" integrity sha1-eOy4o5kGYYe7N07t412ccFZagD0= -electron@9.2.1: - version "9.2.1" - resolved "https://registry.yarnpkg.com/electron/-/electron-9.2.1.tgz#54ef574e1af4ae967b5efa94312f1b6458d44a02" - integrity sha512-ZsetaQjXB8+9/EFW1FnfK4ukpkwXCxMEaiKiUZhZ0ZLFlLnFCpe0Bg4vdDf7e4boWGcnlgN1jAJpBw7w0eXuqA== +electron@9.3.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/electron/-/electron-9.3.0.tgz#a4f3dc17f31acc6797eb4c2c4bd0d0e25efb939b" + integrity sha512-7zPLEZ+kOjVJqfawMQ0vVuZZRqvZIeiID3tbjjbVybbxXIlFMpZ2jogoh7PV3rLrtm+dKRfu7Qc4E7ob1d0FqQ== dependencies: "@electron/get" "^1.0.1" "@types/node" "^12.0.12" @@ -6288,10 +6288,10 @@ node-pre-gyp@^0.10.0: semver "^5.3.0" tar "^4" -node-pty@0.10.0-beta8: - version "0.10.0-beta8" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta8.tgz#f4aa56c71a794f4580373a3030dfebd25240c6db" - integrity sha512-Ul/hLsadC0SvvShxpne+kq2ebSMcitewlNhrwoXXBvFdCqxJt7Ai1AgMhH7AKBUp06uBeYXThJ2ihTszrkdnYw== +node-pty@0.10.0-beta17: + version "0.10.0-beta17" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta17.tgz#962d4a3f4dc6772385e0cad529c209cef3bc79e6" + integrity sha512-tn7EANQacnAvnOQCImvgag1DL0tVmUoY/1yIZbh3u/BBpvCcGHLZJNn7TXheodRLr6hmGSUS2VbfcUr9p0gOug== dependencies: nan "^2.14.0" @@ -9724,10 +9724,10 @@ vscode-proxy-agent@^0.5.2: https-proxy-agent "^2.2.3" socks-proxy-agent "^4.0.1" -vscode-ripgrep@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.8.0.tgz#dfe7c2ae2a2032df8a8108765c2feef73474888a" - integrity sha512-/Q5XtePkTLLi8yplr5ai24pVEymRF62xH9xXrtj35GTaDCJg3zq1s1/L1UqhVbfNDv4OcMBYjyIAt/quEi3d5w== +vscode-ripgrep@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.9.0.tgz#d6cdea4d290f3c2919472cdcfe2440d5fb1f99db" + integrity sha512-7jyAC/NNfvMPZgCVkyqIn0STYJ7wIk3PF2qA2cX1sEutx1g/e2VtgKAodXnfpreJq4993JT/BSIigOv/0lBSzg== dependencies: https-proxy-agent "^4.0.0" proxy-from-env "^1.1.0" @@ -10060,15 +10060,15 @@ xterm-addon-unicode11@0.2.0: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0.tgz#9ed0c482b353908bba27778893ca80823382737c" integrity sha512-rjFDItPc/IDoSiEnoDFwKroNwLD/7t9vYKENjrcKVZg5tgJuuUj8D4rZtP6iVCjSB1LTLYmUs4L/EmCqIyLR/Q== -xterm-addon-webgl@0.9.0-beta.1: - version "0.9.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.9.0-beta.1.tgz#e0a4e9b71db7ef8182064236844caf32833214c2" - integrity sha512-yyIYfehzaqaOTOTjSapA3sEcaGDIPn0xAiWyRIWyW5zwTZIo7FA+O4IzhgAT9b7ioJWLcPoHPUA0OFNHAx20vQ== +xterm-addon-webgl@0.9.0-beta.4: + version "0.9.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.9.0-beta.4.tgz#5f5fde50db5c06b116471bcf56ad9930884b36fa" + integrity sha512-GuCvF7Eg1nKLX6zUbJLkt5cqeeccUjf/G6fugCfrkR0EWWC6Ik5mEsEOs5UWm9vvY2kX9t16BdCrgqp8KJegEg== -xterm@4.9.0-beta.25: - version "4.9.0-beta.25" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.25.tgz#c229a144184ab7b3c3afdaff76f9d81bac58933b" - integrity sha512-NJHwuS7Qsoq/Yuobl+Au4rgol/oBNj4pGoR2R6IRnXODS5RDE4spHDofWKiUkDMlNZtPPku1W7k1kFnonDIhhg== +xterm@4.9.0-beta.32: + version "4.9.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.32.tgz#d1243d3be211cc06aad3418e696e4eced995c20c" + integrity sha512-jloHNBnj6XRJt+oPkapvrXJZVsYq6se/PEgzErl0iZn9qzSB3jmWE4byumoEjXJR6EgU5ZOmNljeeEDA9jO/jA== y18n@^3.2.1: version "3.2.1"