Skip to content

Commit 6c4e2f9

Browse files
code-asherkylecarbs
authored andcommitted
Terminal pasting
1 parent bef4639 commit 6c4e2f9

6 files changed

Lines changed: 125 additions & 27 deletions

File tree

packages/ide/src/client.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Event } from "@coder/events";
22
import { field, logger, time, Time } from "@coder/logger";
33
import { InitData, ISharedProcessData } from "@coder/protocol";
44
import { retry } from "./retry";
5-
import { Upload } from "./upload";
5+
import { upload } from "./upload";
66
import { client } from "./fill/client";
77
import { clipboard } from "./fill/clipboard";
88
import { INotificationService, NotificationService, IProgressService, ProgressService } from "./fill/notification";
@@ -21,7 +21,8 @@ export abstract class Client {
2121
public readonly retry = retry;
2222
public readonly clipboard = clipboard;
2323
public readonly uriFactory: IURIFactory;
24-
public readonly upload = new Upload(new NotificationService(), new ProgressService());
24+
public readonly upload = upload;
25+
2526
private start: Time | undefined;
2627
private readonly progressElement: HTMLElement | undefined;
2728
private tasks: string[] = [];

packages/ide/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
export * from "./client";
2-
export * from "./fill/uri";
2+
export * from "./fill/clipboard";
33
export * from "./fill/notification";
4+
export * from "./fill/uri";
5+
export * from "./retry";
6+
export * from "./upload";

packages/ide/src/upload.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { promisify } from "util";
44
import { logger, Logger } from "@coder/logger";
55
import { escapePath } from "@coder/protocol";
66
import { IURI } from "./fill/uri";
7-
import { INotificationService, IProgressService, IProgress, Severity } from "./fill/notification";
7+
import { NotificationService, INotificationService, ProgressService, IProgressService, IProgress, Severity } from "./fill/notification";
88

99
/**
1010
* Represents an uploadable directory, so we can query for existing files once.
@@ -355,3 +355,6 @@ export class Upload {
355355
}
356356

357357
}
358+
359+
// Global instance.
360+
export const upload = new Upload(new NotificationService(), new ProgressService());

packages/vscode/src/client.ts

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import "./fill/storageDatabase";
44
import "./fill/windowsService";
55
import "./fill/environmentService";
66
import "./fill/vscodeTextmate";
7+
import { PasteAction } from "./fill/paste";
78
import "./fill/dom";
89
import "./vscode.scss";
910
import { Client as IDEClient, IURI, IURIFactory, IProgress, INotificationHandle } from "@coder/ide";
@@ -19,8 +20,6 @@ import { IEditorGroup } from "vs/workbench/services/group/common/editorGroupsSer
1920
import { IWindowsService } from "vs/platform/windows/common/windows";
2021
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
2122
import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey";
22-
import { Action } from "vs/base/common/actions";
23-
import * as nls from "vs/nls";
2423

2524
export class Client extends IDEClient {
2625

@@ -78,27 +77,8 @@ export class Client extends IDEClient {
7877
/**
7978
* Create a paste action for use in text inputs.
8079
*/
81-
public get pasteAction(): Action {
82-
const getLabel = (enabled: boolean): string => {
83-
return enabled
84-
? nls.localize("paste", "Paste")
85-
: nls.localize("pasteWithKeybind", "Paste (must use keybind)");
86-
};
87-
88-
const pasteAction = new Action(
89-
"editor.action.clipboardPasteAction",
90-
getLabel(this.clipboard.isEnabled),
91-
undefined,
92-
this.clipboard.isEnabled,
93-
async (): Promise<boolean> => this.clipboard.paste(),
94-
);
95-
96-
this.clipboard.onPermissionChange((enabled) => {
97-
pasteAction.label = getLabel(enabled);
98-
pasteAction.enabled = enabled;
99-
});
100-
101-
return pasteAction;
80+
public get pasteAction(): PasteAction {
81+
return new PasteAction();
10282
}
10383

10484
public get serviceCollection(): ServiceCollection {

packages/vscode/src/fill/paste.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import * as nls from "vs/nls";
2+
import { Action } from "vs/base/common/actions";
3+
import { TERMINAL_COMMAND_ID } from "vs/workbench/parts/terminal/common/terminalCommands";
4+
import { ITerminalService } from "vs/workbench/parts/terminal/common/terminal";
5+
import * as actions from "vs/workbench/parts/terminal/electron-browser/terminalActions";
6+
import * as instance from "vs/workbench/parts/terminal/electron-browser/terminalInstance";
7+
import { clipboard } from "@coder/ide";
8+
9+
const getLabel = (key: string, enabled: boolean): string => {
10+
return enabled
11+
? nls.localize(key, "Paste")
12+
: nls.localize(`${key}WithKeybind`, "Paste (must use keybind)");
13+
};
14+
15+
export class PasteAction extends Action {
16+
17+
private static readonly KEY = "paste";
18+
19+
public constructor() {
20+
super(
21+
"editor.action.clipboardPasteAction",
22+
getLabel(PasteAction.KEY, clipboard.isEnabled),
23+
undefined,
24+
clipboard.isEnabled,
25+
async (): Promise<boolean> => clipboard.paste(),
26+
);
27+
28+
clipboard.onPermissionChange((enabled) => {
29+
this.label = getLabel(PasteAction.KEY, enabled);
30+
this.enabled = enabled;
31+
});
32+
}
33+
34+
}
35+
36+
class TerminalPasteAction extends Action {
37+
38+
private static readonly KEY = "workbench.action.terminal.paste";
39+
40+
public static readonly ID = TERMINAL_COMMAND_ID.PASTE;
41+
public static readonly LABEL = nls.localize("workbench.action.terminal.paste", "Paste into Active Terminal");
42+
public static readonly SHORT_LABEL = getLabel(TerminalPasteAction.KEY, clipboard.isEnabled);
43+
44+
public constructor(
45+
id: string, label: string,
46+
@ITerminalService private terminalService: ITerminalService,
47+
) {
48+
super(id, label);
49+
clipboard.onPermissionChange((enabled) => {
50+
this._setLabel(getLabel(TerminalPasteAction.KEY, enabled));
51+
});
52+
this._setLabel(getLabel(TerminalPasteAction.KEY, clipboard.isEnabled));
53+
}
54+
55+
public run(): Promise<void> {
56+
const instance = this.terminalService.getActiveOrCreateInstance();
57+
if (instance) {
58+
// tslint:disable-next-line no-any it will return a promise (see below)
59+
return (instance as any).paste();
60+
}
61+
62+
return Promise.resolve();
63+
}
64+
65+
}
66+
67+
class TerminalInstance extends instance.TerminalInstance {
68+
69+
public async paste(): Promise<void> {
70+
this.focus();
71+
if (clipboard.isEnabled) {
72+
const text = await clipboard.readText();
73+
this.sendText(text, false);
74+
} else {
75+
document.execCommand("paste");
76+
}
77+
}
78+
79+
}
80+
81+
const actionsTarget = actions as typeof actions;
82+
// @ts-ignore TODO: don't ignore it.
83+
actionsTarget.TerminalPasteAction = TerminalPasteAction;
84+
85+
const instanceTarget = instance as typeof instance;
86+
instanceTarget.TerminalInstance = TerminalInstance;

scripts/vscode.patch

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,31 @@ index e600fb2f78..5d65a3124e 100644
229229
const droppedResources = extractResources(originalEvent.browserEvent as DragEvent, true);
230230

231231
// Check for dropped external files to be folders
232+
diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts
233+
index 2975294e75..73ffb6362d 100644
234+
--- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts
235+
+++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts
236+
@@ -38,6 +38,7 @@ import { TerminalPanel } from 'vs/workbench/parts/terminal/electron-browser/term
237+
import { TerminalPickerHandler } from 'vs/workbench/parts/terminal/browser/terminalQuickOpen';
238+
import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands';
239+
import { setupTerminalMenu } from 'vs/workbench/parts/terminal/common/terminalMenu';
240+
+import { client } from "../../../../../../../../packages/vscode";
241+
242+
const quickOpenRegistry = (Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen));
243+
244+
@@ -434,9 +435,11 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousTer
245+
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.LABEL, {
246+
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
247+
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V },
248+
+ win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V },
249+
// Don't apply to Mac since cmd+v works
250+
mac: { primary: 0 }
251+
-}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category);
252+
+// }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category);
253+
+}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, client.clipboardContextKey)), 'Terminal: Paste into Active Terminal', category);
254+
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL, {
255+
// Don't use ctrl+a by default as that would override the common go to start
256+
// of prompt shell binding
232257
diff --git a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts
233258
index 7b4e8721ac..8f26dc2f28 100644
234259
--- a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts

0 commit comments

Comments
 (0)