Skip to content

Commit 5cc4bc6

Browse files
authored
1 parent 600fe33 commit 5cc4bc6

File tree

4 files changed

+36
-14
lines changed

4 files changed

+36
-14
lines changed

src/vs/workbench/contrib/terminal/browser/terminal.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
1515
import { IEditableData } from 'vs/workbench/common/views';
1616
import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
1717
import { IKeyMods } from 'vs/platform/quickinput/common/quickInput';
18-
import { ITerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/capabilities';
18+
import { ITerminalCapabilityStore, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities';
1919
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
2020
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
2121

@@ -651,7 +651,7 @@ export interface ITerminalInstance {
651651
/**
652652
* Copies the terminal selection to the clipboard.
653653
*/
654-
copySelection(asHtml?: boolean): Promise<void>;
654+
copySelection(asHtml?: boolean, command?: ITerminalCommand): Promise<void>;
655655

656656
/**
657657
* Current selection in the terminal.

src/vs/workbench/contrib/terminal/browser/terminalInstance.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
654654
const lineDataEventAddon = new LineDataEventAddon();
655655
this.xterm.raw.loadAddon(lineDataEventAddon);
656656
this.updateAccessibilitySupport();
657-
this.xterm.onDidRequestRunCommand(command => this.sendText(command, true));
657+
this.xterm.onDidRequestRunCommand(e => {
658+
if (e.copyAsHtml) {
659+
this.copySelection(true, e.command);
660+
} else {
661+
this.sendText(e.command.command, true);
662+
}
663+
});
658664
// Write initial text, deferring onLineFeed listener when applicable to avoid firing
659665
// onLineData events containing initialText
660666
if (this._shellLaunchConfig.initialText) {
@@ -1145,13 +1151,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
11451151
return this.xterm ? this.xterm.raw.hasSelection() : false;
11461152
}
11471153

1148-
async copySelection(asHtml?: boolean): Promise<void> {
1154+
async copySelection(asHtml?: boolean, command?: ITerminalCommand): Promise<void> {
11491155
const xterm = await this._xtermReadyPromise;
1150-
if (this.hasSelection()) {
1156+
if (this.hasSelection() || (asHtml && command)) {
11511157
if (asHtml) {
1152-
const selectionAsHtml = await xterm.getSelectionAsHtml();
1158+
const textAsHtml = await xterm.getSelectionAsHtml(command);
11531159
function listener(e: any) {
1154-
e.clipboardData.setData('text/html', selectionAsHtml);
1160+
e.clipboardData.setData('text/html', textAsHtml);
11551161
e.preventDefault();
11561162
}
11571163
document.addEventListener('copy', listener);

src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
4949
private _decorations: Map<number, IDisposableDecoration> = new Map();
5050
private _placeholderDecoration: IDecoration | undefined;
5151

52-
private readonly _onDidRequestRunCommand = this._register(new Emitter<string>());
52+
private readonly _onDidRequestRunCommand = this._register(new Emitter<{ command: ITerminalCommand; copyAsHtml?: boolean }>());
5353
readonly onDidRequestRunCommand = this._onDidRequestRunCommand.event;
5454

5555
constructor(
@@ -313,10 +313,14 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
313313
class: 'copy-output', tooltip: 'Copy Output', dispose: () => { }, id: 'terminal.copyOutput', label: localize("terminal.copyOutput", 'Copy Output'), enabled: true,
314314
run: () => this._clipboardService.writeText(command.getOutput()!)
315315
});
316+
actions.push({
317+
class: 'copy-output', tooltip: 'Copy Output as HTML', dispose: () => { }, id: 'terminal.copyOutputAsHtml', label: localize("terminal.copyOutputAsHtml", 'Copy Output as HTML'), enabled: true,
318+
run: () => this._onDidRequestRunCommand.fire({ command, copyAsHtml: true })
319+
});
316320
}
317321
actions.push({
318322
class: 'rerun-command', tooltip: 'Rerun Command', dispose: () => { }, id: 'terminal.rerunCommand', label: localize("terminal.rerunCommand", 'Rerun Command'), enabled: true,
319-
run: () => this._onDidRequestRunCommand.fire(command.command)
323+
run: () => this._onDidRequestRunCommand.fire({ command })
320324
});
321325
return actions;
322326
}

src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { Color } from 'vs/base/common/color';
3232
import { ShellIntegrationAddon } from 'vs/platform/terminal/common/xterm/shellIntegrationAddon';
3333
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
3434
import { DecorationAddon } from 'vs/workbench/contrib/terminal/browser/xterm/decorationAddon';
35-
import { ITerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/capabilities';
35+
import { ITerminalCapabilityStore, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities';
3636
import { Emitter } from 'vs/base/common/event';
3737

3838
// How long in milliseconds should an average frame take to render for a notification to appear
@@ -71,7 +71,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
7171
private _lastFindResult: { resultIndex: number; resultCount: number } | undefined;
7272
get findResult(): { resultIndex: number; resultCount: number } | undefined { return this._lastFindResult; }
7373

74-
private readonly _onDidRequestRunCommand = new Emitter<string>();
74+
private readonly _onDidRequestRunCommand = new Emitter<{ command: ITerminalCommand; copyAsHtml?: boolean }>();
7575
readonly onDidRequestRunCommand = this._onDidRequestRunCommand.event;
7676

7777
private readonly _onDidChangeFindResults = new Emitter<{ resultIndex: number; resultCount: number } | undefined>();
@@ -175,17 +175,29 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
175175
}
176176
private _createDecorationAddon(): void {
177177
this._decorationAddon = this._instantiationService.createInstance(DecorationAddon, this._capabilities);
178-
this._decorationAddon.onDidRequestRunCommand(command => this._onDidRequestRunCommand.fire(command));
178+
this._decorationAddon.onDidRequestRunCommand(e => this._onDidRequestRunCommand.fire(e));
179179
this.raw.loadAddon(this._decorationAddon);
180180
}
181181

182-
async getSelectionAsHtml(): Promise<string> {
182+
async getSelectionAsHtml(command?: ITerminalCommand): Promise<string> {
183183
if (!this._serializeAddon) {
184184
const Addon = await this._getSerializeAddonConstructor();
185185
this._serializeAddon = new Addon();
186186
this.raw.loadAddon(this._serializeAddon);
187187
}
188-
return this._serializeAddon.serializeAsHTML({ onlySelection: true });
188+
if (command) {
189+
const length = command.getOutput()?.length;
190+
const row = command.marker?.line;
191+
if (!length || !row) {
192+
throw new Error(`No row ${row} or output length ${length} for command ${command}`);
193+
}
194+
await this.raw.select(0, row + 1, length - Math.floor(length / this.raw.cols));
195+
}
196+
const result = this._serializeAddon.serializeAsHTML({ onlySelection: true });
197+
if (command) {
198+
this.raw.clearSelection();
199+
}
200+
return result;
189201
}
190202

191203
attachToElement(container: HTMLElement): HTMLElement {

0 commit comments

Comments
 (0)