diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index ecac68568c4..a276eaca322 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1273,7 +1273,7 @@ export class Repository implements Disposable { const diffEditorTabsToClose: Tab[] = []; // Index - const tabs = window.tabGroups.all.map(g => g.tabs).flat(1); + const tabs = window.tabGroups.groups.map(g => g.tabs).flat(1); diffEditorTabsToClose.push(...tabs .filter(t => t.resource && t.resource.scheme === 'git' && t.kind === TabKind.Diff && diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index dd5a8ed708b..7e4c3cd5a7e 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vscode-nls'; -import { CancellationToken, ConfigurationChangeEvent, Disposable, env, Event, EventEmitter, ThemeIcon, Timeline, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvider, Uri, workspace } from 'vscode'; +import { CancellationToken, ConfigurationChangeEvent, Disposable, env, Event, EventEmitter, MarkdownString, ThemeIcon, Timeline, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvider, Uri, workspace } from 'vscode'; import { Model } from './model'; import { Repository, Resource } from './repository'; import { debounce } from './decorators'; @@ -50,6 +50,20 @@ export class GitTimelineItem extends TimelineItem { return this.shortenRef(this.previousRef); } + setItemDetails(author: string, email: string | undefined, date: string, message: string): void { + this.detail = new MarkdownString('', true); + + if (email) { + const emailTitle = localize('git.timeline.email', "Email"); + this.detail.appendMarkdown(`$(account) [**${author}**](mailto:${email} "${emailTitle} ${author}")\n\n`); + } else { + this.detail.appendMarkdown(`$(account) **${author}**\n\n`); + } + + this.detail.appendMarkdown(`$(history) ${date}\n\n`); + this.detail.appendMarkdown(message); + } + private shortenRef(ref: string): string { if (ref === '' || ref === '~' || ref === 'HEAD') { return ref; @@ -167,7 +181,8 @@ export class GitTimelineProvider implements TimelineProvider { if (showAuthor) { item.description = c.authorName; } - item.detail = `${c.authorName} (${c.authorEmail}) \u2014 ${c.hash.substr(0, 8)}\n${dateFormatter.format(date)}\n\n${message}`; + + item.setItemDetails(c.authorName!, c.authorEmail, dateFormatter.format(date), message); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { @@ -192,7 +207,7 @@ export class GitTimelineProvider implements TimelineProvider { // TODO@eamodio: Replace with a better icon -- reflecting its status maybe? item.iconPath = new ThemeIcon('git-commit'); item.description = ''; - item.detail = localize('git.timeline.detail', '{0} \u2014 {1}\n{2}\n\n{3}', you, localize('git.index', 'Index'), dateFormatter.format(date), Resource.getStatusText(index.type)); + item.setItemDetails(you, undefined, dateFormatter.format(date), Resource.getStatusText(index.type)); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { @@ -214,7 +229,7 @@ export class GitTimelineProvider implements TimelineProvider { // TODO@eamodio: Replace with a better icon -- reflecting its status maybe? item.iconPath = new ThemeIcon('git-commit'); item.description = ''; - item.detail = localize('git.timeline.detail', '{0} \u2014 {1}\n{2}\n\n{3}', you, localize('git.workingTree', 'Working Tree'), dateFormatter.format(date), Resource.getStatusText(working.type)); + item.setItemDetails(you, undefined, dateFormatter.format(date), Resource.getStatusText(working.type)); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { diff --git a/extensions/lua/cgmanifest.json b/extensions/lua/cgmanifest.json index 432d82cc8bf..153296f8cce 100644 --- a/extensions/lua/cgmanifest.json +++ b/extensions/lua/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "sumneko/lua.tmbundle", "repositoryUrl": "https://github.com/sumneko/lua.tmbundle", - "commitHash": "9b79cbc54c3267065fca3d7b5b3845d1efbd091d" + "commitHash": "43da68bb99b948782e57985f633fa0c395190674" } }, "licenseDetail": [ diff --git a/extensions/lua/syntaxes/lua.tmLanguage.json b/extensions/lua/syntaxes/lua.tmLanguage.json index 23404451fc9..05c4b16814f 100644 --- a/extensions/lua/syntaxes/lua.tmLanguage.json +++ b/extensions/lua/syntaxes/lua.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/sumneko/lua.tmbundle/commit/9b79cbc54c3267065fca3d7b5b3845d1efbd091d", + "version": "https://github.com/sumneko/lua.tmbundle/commit/43da68bb99b948782e57985f633fa0c395190674", "name": "Lua", "scopeName": "source.lua", "patterns": [ @@ -170,10 +170,6 @@ "match": "(? { await commands.executeCommand('vscode.open', uri); assert.strictEqual(window.activeTextEditor?.viewColumn, ViewColumn.One); - assert.strictEqual(window.tabGroups.all[0].activeTab?.viewColumn, ViewColumn.One); + assert.strictEqual(window.tabGroups.groups[0].activeTab?.viewColumn, ViewColumn.One); await commands.executeCommand('vscode.open', uri, ViewColumn.Two); assert.strictEqual(window.activeTextEditor?.viewColumn, ViewColumn.Two); - assert.strictEqual(window.tabGroups.all[1].activeTab?.viewColumn, ViewColumn.Two); + assert.strictEqual(window.tabGroups.groups[1].activeTab?.viewColumn, ViewColumn.Two); await commands.executeCommand('vscode.open', uri, ViewColumn.One); assert.strictEqual(window.activeTextEditor?.viewColumn, ViewColumn.One); - assert.strictEqual(window.tabGroups.all[0].activeTab?.viewColumn, ViewColumn.One); + assert.strictEqual(window.tabGroups.groups[0].activeTab?.viewColumn, ViewColumn.One); let e1: Error | undefined = undefined; try { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index 3c1d22d5202..38565c017f5 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -383,12 +383,12 @@ suite('vscode API - window', () => { await window.showTextDocument(docC, { viewColumn: ViewColumn.Two, preview: false }); const tabGroups = window.tabGroups; - assert.strictEqual(tabGroups.all.length, 2); + assert.strictEqual(tabGroups.groups.length, 2); - const group1Tabs = tabGroups.all[0].tabs; + const group1Tabs = tabGroups.groups[0].tabs; assert.strictEqual(group1Tabs.length, 2); - const group2Tabs = tabGroups.all[1].tabs; + const group2Tabs = tabGroups.groups[1].tabs; assert.strictEqual(group2Tabs.length, 1); await group1Tabs[0].move(1, ViewColumn.One); @@ -397,7 +397,7 @@ suite('vscode API - window', () => { test('Tabs - vscode.open & vscode.diff', async function () { // Simple function to get the active tab const getActiveTab = () => { - return window.tabGroups.all.find(g => g.isActive)?.activeTab; + return window.tabGroups.groups.find(g => g.isActive)?.activeTab; }; const [docA, docB, docC] = await Promise.all([ @@ -420,7 +420,7 @@ suite('vscode API - window', () => { await commands.executeCommand('vscode.diff', leftDiff, rightDiff, 'Diff', { viewColumn: ViewColumn.Four, preview: false }); assert.strictEqual(getActiveTab()?.viewColumn, ViewColumn.Four); - const tabs = window.tabGroups.all.map(g => g.tabs).flat(1); + const tabs = window.tabGroups.groups.map(g => g.tabs).flat(1); assert.strictEqual(tabs.length, 5); assert.strictEqual(tabs[0].resource?.toString(), docA.uri.toString()); assert.strictEqual(tabs[1].resource?.toString(), docB.uri.toString()); @@ -451,7 +451,7 @@ suite('vscode API - window', () => { const rightDiff = await createRandomFile(); await commands.executeCommand('vscode.diff', leftDiff, rightDiff, 'Diff', { viewColumn: ViewColumn.Three, preview: false }); - const tabs = window.tabGroups.all.map(g => g.tabs).flat(1); + const tabs = window.tabGroups.groups.map(g => g.tabs).flat(1); assert.strictEqual(tabs.length, 5); // All resources should match the text documents as they're the only tabs currently open @@ -480,7 +480,7 @@ suite('vscode API - window', () => { // Function to acquire the active tab within the active group const getActiveTabInActiveGroup = () => { - const activeGroup = window.tabGroups.all.filter(group => group.isActive)[0]; + const activeGroup = window.tabGroups.groups.filter(group => group.isActive)[0]; return activeGroup.activeTab; }; diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_lua.json b/extensions/vscode-colorize-tests/test/colorize-results/test_lua.json index 239ef361b3c..9633af1a5e6 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_lua.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_lua.json @@ -409,14 +409,14 @@ }, { "c": "fact", - "t": "source.lua entity.name.function.lua", + "t": "source.lua support.function.any-method.lua", "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA", - "hc_light": "entity.name.function: #795E26" + "hc_black": "support.function: #DCDCAA", + "hc_light": "support.function: #795E26" } }, { @@ -805,14 +805,14 @@ }, { "c": "fact", - "t": "source.lua entity.name.function.lua", + "t": "source.lua support.function.any-method.lua", "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA", - "hc_light": "entity.name.function: #795E26" + "hc_black": "support.function: #DCDCAA", + "hc_light": "support.function: #795E26" } }, { diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index bb67566ac5a..ad5a0f6d37d 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -161,11 +161,11 @@ export class SingleModelEditStackElement implements IResourceUndoRedoElement { return this.model.uri; } - public get label(): string { - return nls.localize('edit', "Typing"); - } - - constructor(model: ITextModel, beforeCursorState: Selection[] | null) { + constructor( + public readonly label: string, + model: ITextModel, + beforeCursorState: Selection[] | null + ) { this.model = model; this._data = SingleModelEditStackData.create(model, beforeCursorState); } @@ -413,7 +413,7 @@ export class EditStack { if (isEditStackElement(lastElement) && lastElement.canAppend(this._model)) { return lastElement; } - const newElement = new SingleModelEditStackElement(this._model, beforeCursorState); + const newElement = new SingleModelEditStackElement(nls.localize('edit', "Typing"), this._model, beforeCursorState); this._undoRedoService.pushElement(newElement); return newElement; } diff --git a/src/vs/editor/common/services/languagesAssociations.ts b/src/vs/editor/common/services/languagesAssociations.ts index 58ad7c3719c..4912ff8c7cf 100644 --- a/src/vs/editor/common/services/languagesAssociations.ts +++ b/src/vs/editor/common/services/languagesAssociations.ts @@ -10,6 +10,7 @@ import { basename, posix } from 'vs/base/common/path'; import { DataUri } from 'vs/base/common/resources'; import { startsWithUTF8BOM } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; +import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; export interface ILanguageAssociation { readonly id: string; @@ -119,11 +120,27 @@ export function clearConfiguredLanguageAssociations(): void { userRegisteredAssociations = []; } +interface IdAndMime { + id: string; + mime: string; +} + /** * Given a file, return the best matching mime types for it * based on the registered language associations. */ export function getMimeTypes(resource: URI | null, firstLine?: string): string[] { + return getAssociations(resource, firstLine).map(item => item.mime); +} + +/** + * @see `getMimeTypes` + */ +export function getLanguageIds(resource: URI | null, firstLine?: string): string[] { + return getAssociations(resource, firstLine).map(item => item.id); +} + +function getAssociations(resource: URI | null, firstLine?: string): IdAndMime[] { let path: string | undefined; if (resource) { switch (resource.scheme) { @@ -141,7 +158,7 @@ export function getMimeTypes(resource: URI | null, firstLine?: string): string[] } if (!path) { - return [Mimes.unknown]; + return [{ id: 'unknown', mime: Mimes.unknown }]; } path = path.toLowerCase(); @@ -149,29 +166,29 @@ export function getMimeTypes(resource: URI | null, firstLine?: string): string[] const filename = basename(path); // 1.) User configured mappings have highest priority - const configuredLanguage = getMimeByPath(path, filename, userRegisteredAssociations); + const configuredLanguage = getAssociationByPath(path, filename, userRegisteredAssociations); if (configuredLanguage) { - return [configuredLanguage, Mimes.text]; + return [configuredLanguage, { id: PLAINTEXT_LANGUAGE_ID, mime: Mimes.text }]; } // 2.) Registered mappings have middle priority - const registeredLanguage = getMimeByPath(path, filename, nonUserRegisteredAssociations); + const registeredLanguage = getAssociationByPath(path, filename, nonUserRegisteredAssociations); if (registeredLanguage) { - return [registeredLanguage, Mimes.text]; + return [registeredLanguage, { id: PLAINTEXT_LANGUAGE_ID, mime: Mimes.text }]; } // 3.) Firstline has lowest priority if (firstLine) { - const firstlineLanguage = getMimeByFirstline(firstLine); + const firstlineLanguage = getAssociationByFirstline(firstLine); if (firstlineLanguage) { - return [firstlineLanguage, Mimes.text]; + return [firstlineLanguage, { id: PLAINTEXT_LANGUAGE_ID, mime: Mimes.text }]; } } - return [Mimes.unknown]; + return [{ id: 'unknown', mime: Mimes.unknown }]; } -function getMimeByPath(path: string, filename: string, associations: ILanguageAssociationItem[]): string | undefined { +function getAssociationByPath(path: string, filename: string, associations: ILanguageAssociationItem[]): ILanguageAssociationItem | undefined { let filenameMatch: ILanguageAssociationItem | undefined = undefined; let patternMatch: ILanguageAssociationItem | undefined = undefined; let extensionMatch: ILanguageAssociationItem | undefined = undefined; @@ -209,23 +226,23 @@ function getMimeByPath(path: string, filename: string, associations: ILanguageAs // 1.) Exact name match has second highest priority if (filenameMatch) { - return filenameMatch.mime; + return filenameMatch; } // 2.) Match on pattern if (patternMatch) { - return patternMatch.mime; + return patternMatch; } // 3.) Match on extension comes next if (extensionMatch) { - return extensionMatch.mime; + return extensionMatch; } return undefined; } -function getMimeByFirstline(firstLine: string): string | undefined { +function getAssociationByFirstline(firstLine: string): ILanguageAssociationItem | undefined { if (startsWithUTF8BOM(firstLine)) { firstLine = firstLine.substr(1); } @@ -242,7 +259,7 @@ function getMimeByFirstline(firstLine: string): string | undefined { const matches = firstLine.match(association.firstline); if (matches && matches.length > 0) { - return association.mime; + return association; } } } diff --git a/src/vs/editor/common/services/languagesRegistry.ts b/src/vs/editor/common/services/languagesRegistry.ts index 138fe2e8bb5..01b58ce512c 100644 --- a/src/vs/editor/common/services/languagesRegistry.ts +++ b/src/vs/editor/common/services/languagesRegistry.ts @@ -3,12 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { coalesce } from 'vs/base/common/arrays'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { compareIgnoreCase, regExpLeadsToEndlessLoop } from 'vs/base/common/strings'; -import { clearPlatformLanguageAssociations, getMimeTypes, registerPlatformLanguageAssociation } from 'vs/editor/common/services/languagesAssociations'; +import { clearPlatformLanguageAssociations, getLanguageIds, registerPlatformLanguageAssociation } from 'vs/editor/common/services/languagesAssociations'; import { URI } from 'vs/base/common/uri'; import { ILanguageIdCodec, LanguageId } from 'vs/editor/common/languages'; import { ModesRegistry, PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; @@ -362,7 +361,6 @@ export class LanguagesRegistry extends Disposable { if (!resource && !firstLine) { return []; } - const mimeTypes = getMimeTypes(resource, firstLine); - return coalesce(mimeTypes.map(mimeType => this.getLanguageIdByMimeType(mimeType))); + return getLanguageIds(resource, firstLine); } } diff --git a/src/vs/workbench/api/browser/mainThreadEditorTabs.ts b/src/vs/workbench/api/browser/mainThreadEditorTabs.ts index c6d956194f3..3b4fef9a358 100644 --- a/src/vs/workbench/api/browser/mainThreadEditorTabs.ts +++ b/src/vs/workbench/api/browser/mainThreadEditorTabs.ts @@ -47,7 +47,6 @@ export class MainThreadEditorTabs { * @returns A tab object */ private _buildTabObject(group: IEditorGroup, editor: EditorInput, editorIndex: number): IEditorTabDto { - // Even though the id isn't a diff / sideBySide on the main side we need to let the ext host know what type of editor it is const editorId = editor.editorId; const tabKind = editor instanceof DiffEditorInput ? TabKind.Diff : editor instanceof SideBySideEditorInput ? TabKind.SidebySide : TabKind.Singular; const tab: IEditorTabDto = { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 73db462b209..d4419a85614 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -628,8 +628,7 @@ export interface IEditorTabGroupDto { export enum TabKind { Singular = 0, Diff = 1, - SidebySide = 2, - Other = 3 + SidebySide = 2 } export interface IEditorTabDto { diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 26314660114..c0d3d9a3ea3 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -953,7 +953,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ if (activeEditor) { return activeEditor.document.uri; } - const activeTab = editorTabs.tabGroups.all.find(group => group.isActive)?.activeTab; + const activeTab = editorTabs.tabGroups.groups.find(group => group.isActive)?.activeTab; if (activeTab !== undefined) { // Resolve a resource from the tab const asSideBySideResource = activeTab.resource as { primary?: URI; secondary?: URI } | undefined; diff --git a/src/vs/workbench/api/common/extHostEditorTabs.ts b/src/vs/workbench/api/common/extHostEditorTabs.ts index 5d6033c4849..e3b5fb3d5e9 100644 --- a/src/vs/workbench/api/common/extHostEditorTabs.ts +++ b/src/vs/workbench/api/common/extHostEditorTabs.ts @@ -33,7 +33,7 @@ export interface IEditorTabGroup { } export interface IEditorTabGroups { - all: IEditorTabGroup[]; + groups: IEditorTabGroup[]; activeTabGroup: IEditorTabGroup | undefined; readonly onDidChangeTabGroup: Event; readonly onDidChangeActiveTabGroup: Event; @@ -55,7 +55,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { private readonly _onDidChangeActiveTabGroup = new Emitter(); private _tabGroups: IEditorTabGroups = { - all: [], + groups: [], activeTabGroup: undefined, onDidChangeTabGroup: this._onDidChangeTabGroup.event, onDidChangeActiveTabGroup: this._onDidChangeActiveTabGroup.event @@ -71,7 +71,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { $acceptEditorTabModel(tabGroups: IEditorTabGroupDto[]): void { // Clears the tab groups array - this._tabGroups.all.length = 0; + this._tabGroups.groups.length = 0; let activeGroupFound = false; for (const group of tabGroups) { let activeTab: IEditorTab | undefined; @@ -82,7 +82,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { } return extHostTab; }); - this._tabGroups.all.push(Object.freeze({ + this._tabGroups.groups.push(Object.freeze({ isActive: group.isActive, viewColumn: typeConverters.ViewColumn.to(group.viewColumn), activeTab, @@ -93,7 +93,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { if (group.isActive) { activeGroupFound = true; const oldActiveTabGroup = this._tabGroups.activeTabGroup; - this._tabGroups.activeTabGroup = this._tabGroups.all[this._tabGroups.all.length - 1]; + this._tabGroups.activeTabGroup = this._tabGroups.groups[this._tabGroups.groups.length - 1]; // Diff the old and current active group to decide if we should fire a change event if (this.groupDiff(oldActiveTabGroup, this._tabGroups.activeTabGroup)) { this._onDidChangeActiveTabGroup.fire(this._tabGroups.activeTabGroup); diff --git a/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts b/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts index ed27b7b6dec..359ecb35c55 100644 --- a/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts +++ b/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts @@ -21,7 +21,7 @@ suite('ExtHostEditorTabs', function () { }) ); - assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 0); + assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 0); assert.strictEqual(extHostEditorTabs.tabGroups.activeTabGroup, undefined); }); @@ -40,7 +40,7 @@ suite('ExtHostEditorTabs', function () { label: 'label1', viewColumn: 0, additionalResourcesAndViewTypes: [], - kind: TabKind.Other + kind: TabKind.Singular }; extHostEditorTabs.$acceptEditorTabModel([{ @@ -50,8 +50,8 @@ suite('ExtHostEditorTabs', function () { tabs: [tab], activeTab: { ...tab } }]); - assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 1); - const [first] = extHostEditorTabs.tabGroups.all; + assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 1); + const [first] = extHostEditorTabs.tabGroups.groups; assert.ok(first.activeTab); assert.strictEqual(first.tabs.indexOf(first.activeTab), 0); @@ -63,8 +63,8 @@ suite('ExtHostEditorTabs', function () { tabs: [tab], activeTab: undefined! // TODO@lramos15 unused }]); - assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 1); - const [first] = extHostEditorTabs.tabGroups.all; + assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 1); + const [first] = extHostEditorTabs.tabGroups.groups; assert.ok(first.activeTab); assert.strictEqual(first.tabs.indexOf(first.activeTab), 0); } @@ -84,8 +84,8 @@ suite('ExtHostEditorTabs', function () { tabs: [], activeTab: undefined }]); - assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 1); - const [first] = extHostEditorTabs.tabGroups.all; + assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 1); + const [first] = extHostEditorTabs.tabGroups.groups; assert.strictEqual(first.activeTab, undefined); assert.strictEqual(first.tabs.length, 0); }); @@ -101,7 +101,7 @@ suite('ExtHostEditorTabs', function () { extHostEditorTabs.tabGroups.onDidChangeTabGroup(() => count++); - assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 0); + assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 0); assert.strictEqual(extHostEditorTabs.tabGroups.activeTabGroup, undefined); assert.strictEqual(count, 0); extHostEditorTabs.$acceptEditorTabModel([{ @@ -113,7 +113,7 @@ suite('ExtHostEditorTabs', function () { }]); assert.ok(extHostEditorTabs.tabGroups.activeTabGroup); const activeTabGroup: IEditorTabGroup = extHostEditorTabs.tabGroups.activeTabGroup; - assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 1); + assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 1); assert.strictEqual(activeTabGroup.tabs.length, 0); assert.strictEqual(count, 1); }); @@ -143,8 +143,8 @@ suite('ExtHostEditorTabs', function () { tabs: [tab], activeTab: { ...tab } }]); - assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 1); - const [first] = extHostEditorTabs.tabGroups.all; + assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 1); + const [first] = extHostEditorTabs.tabGroups.groups; assert.ok(first.activeTab); assert.strictEqual(first.tabs.indexOf(first.activeTab), 0); assert.strictEqual(first.activeTab, first.tabs[0]); @@ -166,7 +166,7 @@ suite('ExtHostEditorTabs', function () { }); - assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 0); + assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 0); assert.strictEqual(extHostEditorTabs.tabGroups.activeTabGroup, undefined); assert.strictEqual(count, 0); const tabModel = [{ @@ -240,7 +240,7 @@ suite('ExtHostEditorTabs', function () { tabs: [tabDto], activeTab: undefined // NOT needed }]); - let all = extHostEditorTabs.tabGroups.all.map(group => group.tabs).flat(); + let all = extHostEditorTabs.tabGroups.groups.map(group => group.tabs).flat(); assert.strictEqual(all.length, 1); const apiTab1 = all[0]; assert.strictEqual(apiTab1.resource?.toString(), URI.revive(tabDto.resource)?.toString()); @@ -258,7 +258,7 @@ suite('ExtHostEditorTabs', function () { activeTab: undefined // NOT needed }]); - all = extHostEditorTabs.tabGroups.all.map(group => group.tabs).flat(); + all = extHostEditorTabs.tabGroups.groups.map(group => group.tabs).flat(); assert.strictEqual(all.length, 1); const apiTab2 = all[0]; assert.strictEqual(apiTab2.resource?.toString(), URI.revive(tabDto.resource)?.toString()); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts index 737c8d2a00d..c4042b8aec8 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts @@ -249,7 +249,7 @@ export class BulkTextEdits { // This edit touches a single model => keep things simple const task = tasks[0]; if (!task.isNoOp()) { - const singleModelEditStackElement = new SingleModelEditStackElement(task.model, task.getBeforeCursorState()); + const singleModelEditStackElement = new SingleModelEditStackElement(this._label, task.model, task.getBeforeCursorState()); this._undoRedoService.pushElement(singleModelEditStackElement, this._undoRedoGroup, this._undoRedoSource); task.apply(); singleModelEditStackElement.close(); @@ -259,7 +259,7 @@ export class BulkTextEdits { // prepare multi model undo element const multiModelEditStackElement = new MultiModelEditStackElement( this._label, - tasks.map(t => new SingleModelEditStackElement(t.model, t.getBeforeCursorState())) + tasks.map(t => new SingleModelEditStackElement(this._label, t.model, t.getBeforeCursorState())) ); this._undoRedoService.pushElement(multiModelEditStackElement, this._undoRedoGroup, this._undoRedoSource); for (const task of tasks) { diff --git a/src/vs/workbench/contrib/comments/common/commentModel.ts b/src/vs/workbench/contrib/comments/common/commentModel.ts index a9c9fbf27ec..4986e959ffe 100644 --- a/src/vs/workbench/contrib/comments/common/commentModel.ts +++ b/src/vs/workbench/contrib/comments/common/commentModel.ts @@ -6,7 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { IRange } from 'vs/editor/common/core/range'; import { Comment, CommentThread, CommentThreadChangedEvent } from 'vs/editor/common/languages'; -import { groupBy, flatten } from 'vs/base/common/arrays'; +import { groupBy } from 'vs/base/common/arrays'; import { localize } from 'vs/nls'; export interface ICommentThreadChangedEvent extends CommentThreadChangedEvent { @@ -71,9 +71,16 @@ export class CommentsModel { this.commentThreadsMap = new Map(); } + private updateResourceCommentThreads() { + this.resourceCommentThreads = [...this.commentThreadsMap.values()].flat(); + this.resourceCommentThreads.sort((a, b) => { + return a.resource.toString() > b.resource.toString() ? 1 : -1; + }); + } + public setCommentThreads(owner: string, commentThreads: CommentThread[]): void { this.commentThreadsMap.set(owner, this.groupByResource(owner, commentThreads)); - this.resourceCommentThreads = flatten([...this.commentThreadsMap.values()]); + this.updateResourceCommentThreads(); } public updateCommentThreads(event: ICommentThreadChangedEvent): boolean { @@ -123,7 +130,7 @@ export class CommentsModel { }); this.commentThreadsMap.set(owner, threadsForOwner); - this.resourceCommentThreads = flatten([...this.commentThreadsMap.values()]); + this.updateResourceCommentThreads(); return removed.length > 0 || changed.length > 0 || added.length > 0; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index a6e9045425c..fd83df23a39 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -221,7 +221,11 @@ export class Extension implements IExtension { } get outdatedTargetPlatform(): boolean { - return !!this.local && !!this.gallery && this.local.targetPlatform !== TargetPlatform.UNDEFINED && this.local.targetPlatform !== this.gallery.properties.targetPlatform && semver.eq(this.latestVersion, this.version); + return !!this.local && !!this.gallery + && ![TargetPlatform.UNDEFINED, TargetPlatform.WEB].includes(this.local.targetPlatform) + && this.gallery.properties.targetPlatform !== TargetPlatform.WEB + && this.local.targetPlatform !== this.gallery.properties.targetPlatform + && semver.eq(this.latestVersion, this.version); } get telemetryData(): any { diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extension.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extension.test.ts index cb37de9dadd..de60b59fab1 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extension.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extension.test.ts @@ -56,6 +56,16 @@ suite('Extension Test', () => { assert.strictEqual(extension.outdated, true); }); + test('extension is not outdated when local and gallery are on same version and local is on web', () => { + const extension = instantiationService.createInstance(Extension, () => ExtensionState.Installed, undefined, aLocalExtension('somext', {}, { targetPlatform: TargetPlatform.WEB }), aGalleryExtension('somext')); + assert.strictEqual(extension.outdated, false); + }); + + test('extension is not outdated when local and gallery are on same version and gallery is on web', () => { + const extension = instantiationService.createInstance(Extension, () => ExtensionState.Installed, undefined, aLocalExtension('somext'), aGalleryExtension('somext', {}, { targetPlatform: TargetPlatform.WEB })); + assert.strictEqual(extension.outdated, false); + }); + test('extension is not outdated when local is not pre-release but gallery is pre-release', () => { const extension = instantiationService.createInstance(Extension, () => ExtensionState.Installed, undefined, aLocalExtension('somext', { version: '1.0.0' }), aGalleryExtension('somext', { version: '1.0.1' }, { isPreReleaseVersion: true })); assert.strictEqual(extension.outdated, false); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 7e598f83d5b..ed4b20be0c8 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -788,7 +788,7 @@ export class SettingsEditor2 extends EditorPane { private createSettingsTree(container: HTMLElement): void { this.settingRenderers = this.instantiationService.createInstance(SettingTreeRenderers); - this._register(this.settingRenderers.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value, e.type))); + this._register(this.settingRenderers.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value, e.type, e.manualReset))); this._register(this.settingRenderers.onDidOpenSettings(settingKey => { this.openSettingsFile({ revealSetting: { key: settingKey, edit: true } }); })); @@ -869,16 +869,16 @@ export class SettingsEditor2 extends EditorPane { })); } - private onDidChangeSetting(key: string, value: any, type: SettingValueType | SettingValueType[]): void { + private onDidChangeSetting(key: string, value: any, type: SettingValueType | SettingValueType[], manualReset: boolean): void { if (this.pendingSettingUpdate && this.pendingSettingUpdate.key !== key) { - this.updateChangedSetting(key, value); + this.updateChangedSetting(key, value, manualReset); } this.pendingSettingUpdate = { key, value }; if (SettingsEditor2.shouldSettingUpdateFast(type)) { - this.settingFastUpdateDelayer.trigger(() => this.updateChangedSetting(key, value)); + this.settingFastUpdateDelayer.trigger(() => this.updateChangedSetting(key, value, manualReset)); } else { - this.settingSlowUpdateDelayer.trigger(() => this.updateChangedSetting(key, value)); + this.settingSlowUpdateDelayer.trigger(() => this.updateChangedSetting(key, value, manualReset)); } } @@ -948,7 +948,7 @@ export class SettingsEditor2 extends EditorPane { return ancestors.reverse(); } - private updateChangedSetting(key: string, value: any): Promise { + private updateChangedSetting(key: string, value: any, manualReset: boolean): Promise { // ConfigurationService displays the error if this fails. // Force a render afterwards because onDidConfigurationUpdate doesn't fire if the update doesn't result in an effective setting value change const settingsTarget = this.settingsTargetsWidget.settingsTarget; @@ -956,11 +956,13 @@ export class SettingsEditor2 extends EditorPane { const configurationTarget = (resource ? ConfigurationTarget.WORKSPACE_FOLDER : settingsTarget); const overrides: IConfigurationOverrides = { resource }; - const isManualReset = value === undefined; + const configurationTargetIsWorkspace = configurationTarget === ConfigurationTarget.WORKSPACE || configurationTarget === ConfigurationTarget.WORKSPACE_FOLDER; - // If the user is changing the value back to the default, do a 'reset' instead + const isManualReset = configurationTargetIsWorkspace ? manualReset : value === undefined; + + // If the user is changing the value back to the default, and we're not targeting a workspace scope, do a 'reset' instead const inspected = this.configurationService.inspect(key, overrides); - if (inspected.defaultValue === value) { + if (!configurationTargetIsWorkspace && inspected.defaultValue === value) { value = undefined; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 83c3b881206..bfc5db90916 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -656,6 +656,7 @@ export interface ISettingChangeEvent { key: string; value: any; // undefined => reset/unconfigure type: SettingValueType | SettingValueType[]; + manualReset: boolean; } export interface ISettingLinkClickEvent { @@ -782,7 +783,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const descriptionElement = DOM.append(container, $('.setting-item-description')); const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator')); - modifiedIndicatorElement.title = localize('modified', "Modified"); + modifiedIndicatorElement.title = localize('modified', "The setting has been configured in the current scope."); const valueElement = DOM.append(container, $('.setting-item-value')); const controlElement = DOM.append(valueElement, $('div.setting-item-control')); @@ -883,7 +884,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre template.miscLabel.updateOtherOverrides(element, template.elementDisposables, this._onDidClickOverrideElement); - const onChange = (value: any) => this._onDidChangeSetting.fire({ key: element.setting.key, value, type: template.context!.valueType }); + const onChange = (value: any) => this._onDidChangeSetting.fire({ key: element.setting.key, value, type: template.context!.valueType, manualReset: false }); const deprecationText = element.setting.deprecationMessage || ''; if (deprecationText && element.setting.deprecationMessageIsMarkdown) { const disposables = new DisposableStore(); @@ -1449,7 +1450,8 @@ export class SettingExcludeRenderer extends AbstractSettingRenderer implements I this._onDidChangeSetting.fire({ key: template.context.setting.key, value: Object.keys(newValue).length === 0 ? undefined : sortKeys(newValue), - type: template.context.valueType + type: template.context.valueType, + manualReset: false }); } } @@ -1765,7 +1767,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre const controlElement = DOM.append(descriptionAndValueElement, $('.setting-item-bool-control')); const descriptionElement = DOM.append(descriptionAndValueElement, $('.setting-item-description')); const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator')); - modifiedIndicatorElement.title = localize('modified', "Modified"); + modifiedIndicatorElement.title = localize('modified', "The setting has been configured in the current scope."); const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); @@ -1904,7 +1906,7 @@ export class SettingTreeRenderers { new Action('settings.resetSetting', localize('resetSettingLabel', "Reset Setting"), undefined, undefined, async context => { if (context instanceof SettingsTreeSettingElement) { if (!context.isUntrusted) { - this._onDidChangeSetting.fire({ key: context.setting.key, value: undefined, type: context.setting.type as SettingValueType }); + this._onDidChangeSetting.fire({ key: context.setting.key, value: undefined, type: context.setting.type as SettingValueType, manualReset: true }); } } }), diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh index 964687b7854..adfdb1e634f 100755 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh @@ -85,16 +85,14 @@ prompt_cmd_original() { STATUS="$?" if [[ "$ORIGINAL_PROMPT_COMMAND" =~ .+\;.+ ]]; then IFS=';' - read -ra ADDR <<<"$ORIGINAL_PROMPT_COMMAND" - for ((i = 0; i < ${#ADDR[@]}; i++)); do - eval ${ADDR[i]} - done - IFS='' else - for i in "${ORIGINAL_PROMPT_COMMAND[@]}"; do - eval $i - done + IFS=' ' fi + read -ra ADDR <<<"$ORIGINAL_PROMPT_COMMAND" + for ((i = 0; i < ${#ADDR[@]}; i++)); do + eval ${ADDR[i]} + done + IFS='' precmd } diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 9702a0cc0c3..6605586e048 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -93,6 +93,10 @@ display: block; } +.monaco-workbench .editor-instance .xterm { + padding-left: 20px !important; +} + .monaco-workbench .pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view:first-child .xterm, .integrated-terminal.shell-integration .xterm { padding-left: 20px !important; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index cfc20e347b5..ca036c6f1b9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -649,7 +649,7 @@ export interface ITerminalInstance { /** * Copies the terminal selection to the clipboard. */ - copySelection(): Promise; + copySelection(asHtml?: boolean): Promise; /** * Current selection in the terminal. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index a05ec3f927d..ed19ad2653a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -2113,6 +2113,20 @@ export function registerTerminalActions() { await accessor.get(ITerminalService).activeInstance?.copySelection(); } }); + registerAction2(class extends Action2 { + constructor() { + super({ + id: TerminalCommandId.CopySelectionAsHtml, + title: { value: localize('workbench.action.terminal.copySelectionAsHtml', "Copy Selection as HTML"), original: 'Copy Selection as HTML' }, + f1: true, + category, + precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.textSelected) + }); + } + async run(accessor: ServicesAccessor) { + await accessor.get(ITerminalService).activeInstance?.copySelection(true); + } + }); } if (BrowserFeatures.clipboard.readText) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index c4b868836cb..dea2f9b1fff 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1126,10 +1126,21 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return this.xterm ? this.xterm.raw.hasSelection() : false; } - async copySelection(): Promise { + async copySelection(asHtml?: boolean): Promise { const xterm = await this._xtermReadyPromise; if (this.hasSelection()) { - await this._clipboardService.writeText(xterm.raw.getSelection()); + if (asHtml) { + const selectionAsHtml = await xterm.getSelectionAsHtml(); + function listener(e: any) { + e.clipboardData.setData('text/html', selectionAsHtml); + e.preventDefault(); + } + document.addEventListener('copy', listener); + document.execCommand('copy'); + document.removeEventListener('copy', listener); + } else { + await this._clipboardService.writeText(xterm.raw.getSelection()); + } } else { this._notificationService.warn(nls.localize('terminal.integrated.copySelection.noSelection', 'The terminal has no selection to copy')); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts index 94bbabfc054..5183542cb14 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts @@ -137,6 +137,17 @@ export function setupTerminalMenus(): void { order: 1 } }, + { + id: MenuId.TerminalInstanceContext, + item: { + command: { + id: TerminalCommandId.CopySelectionAsHtml, + title: localize('workbench.action.terminal.copySelectionAsHtml', "Copy as HTML") + }, + group: ContextMenuGroup.Edit, + order: 2 + } + }, { id: MenuId.TerminalInstanceContext, item: { @@ -145,7 +156,7 @@ export function setupTerminalMenus(): void { title: localize('workbench.action.terminal.paste.short', "Paste") }, group: ContextMenuGroup.Edit, - order: 2 + order: 3 } }, { @@ -237,6 +248,17 @@ export function setupTerminalMenus(): void { order: 1 } }, + { + id: MenuId.TerminalEditorInstanceContext, + item: { + command: { + id: TerminalCommandId.CopySelectionAsHtml, + title: localize('workbench.action.terminal.copySelectionAsHtml', "Copy as HTML") + }, + group: ContextMenuGroup.Edit, + order: 2 + } + }, { id: MenuId.TerminalEditorInstanceContext, item: { @@ -245,7 +267,7 @@ export function setupTerminalMenus(): void { title: localize('workbench.action.terminal.paste.short', "Paste") }, group: ContextMenuGroup.Edit, - order: 2 + order: 3 } }, { diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index feec88f47ac..5bfb629b1be 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -7,6 +7,7 @@ import type { IBuffer, ITheme, RendererType, Terminal as RawXtermTerminal } from import type { ISearchOptions, SearchAddon as SearchAddonType } from 'xterm-addon-search'; import type { Unicode11Addon as Unicode11AddonType } from 'xterm-addon-unicode11'; import type { WebglAddon as WebglAddonType } from 'xterm-addon-webgl'; +import { SerializeAddon as SerializeAddonType } from 'xterm-addon-serialize'; import { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; @@ -42,6 +43,7 @@ const NUMBER_OF_FRAMES_TO_MEASURE = 20; let SearchAddon: typeof SearchAddonType; let Unicode11Addon: typeof Unicode11AddonType; let WebglAddon: typeof WebglAddonType; +let SerializeAddon: typeof SerializeAddonType; /** * Wraps the xterm object with additional functionality. Interaction with the backing process is out @@ -64,6 +66,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { private _searchAddon?: SearchAddonType; private _unicode11Addon?: Unicode11AddonType; private _webglAddon?: WebglAddonType; + private _serializeAddon?: SerializeAddonType; private readonly _onDidRequestRunCommand = new Emitter(); readonly onDidRequestRunCommand = this._onDidRequestRunCommand.event; @@ -167,6 +170,15 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { this.raw.loadAddon(this._decorationAddon); } + async getSelectionAsHtml(): Promise { + if (!this._serializeAddon) { + const Addon = await this._getSerializeAddonConstructor(); + this._serializeAddon = new Addon(); + this.raw.loadAddon(this._serializeAddon); + } + return this._serializeAddon.serializeAsHTML({ onlySelection: true }); + } + attachToElement(container: HTMLElement): HTMLElement { // Update the theme when attaching as the terminal location could have changed this._updateTheme(); @@ -404,6 +416,13 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { return WebglAddon; } + protected async _getSerializeAddonConstructor(): Promise { + if (!SerializeAddon) { + SerializeAddon = (await import('xterm-addon-serialize')).SerializeAddon; + } + return SerializeAddon; + } + private _disposeOfWebglRenderer(): void { try { this._webglAddon?.dispose(); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 3178608dd07..996cfcd801b 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -475,6 +475,7 @@ export const enum TerminalCommandId { RunRecentCommand = 'workbench.action.terminal.runRecentCommand', GoToRecentDirectory = 'workbench.action.terminal.goToRecentDirectory', CopySelection = 'workbench.action.terminal.copySelection', + CopySelectionAsHtml = 'workbench.action.terminal.copySelectionAsHtml', SelectAll = 'workbench.action.terminal.selectAll', DeleteWordLeft = 'workbench.action.terminal.deleteWordLeft', DeleteWordRight = 'workbench.action.terminal.deleteWordRight', @@ -566,6 +567,7 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ TerminalCommandId.ClearSelection, TerminalCommandId.Clear, TerminalCommandId.CopySelection, + TerminalCommandId.CopySelectionAsHtml, TerminalCommandId.DeleteToLineStart, TerminalCommandId.DeleteWordLeft, TerminalCommandId.DeleteWordRight, diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/dark.png b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/dark.png index bac2bc8d2d8..67ad6fcbd35 100644 Binary files a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/dark.png and b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/dark.png differ diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/light.png b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/light.png index 81aa74dc4de..14a472108e5 100644 Binary files a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/light.png and b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/light.png differ diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/monokai.png b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/monokai.png index ea0926178e0..68c4aff78b1 100644 Binary files a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/monokai.png and b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/monokai.png differ diff --git a/src/vscode-dts/vscode.proposed.tabs.d.ts b/src/vscode-dts/vscode.proposed.tabs.d.ts index 292dfb2d6dc..1affd362f79 100644 --- a/src/vscode-dts/vscode.proposed.tabs.d.ts +++ b/src/vscode-dts/vscode.proposed.tabs.d.ts @@ -10,8 +10,7 @@ declare module 'vscode' { export enum TabKind { Singular = 0, Diff = 1, - SidebySide = 2, - Other = 3 + SidebySide = 2 } /** @@ -99,8 +98,7 @@ declare module 'vscode' { /** * All the groups within the group container */ - // TODO@API rename to groups - readonly all: TabGroup[]; + readonly groups: TabGroup[]; /** * The currently active group