mirror of https://github.com/coder/code-server.git
Enable native clipboard for editor and inputs
StackOverflow will be useful again.
This commit is contained in:
parent
ebe5e1b1a9
commit
bef46391fa
|
@ -4,7 +4,7 @@ import { InitData, ISharedProcessData } from "@coder/protocol";
|
||||||
import { retry } from "./retry";
|
import { retry } from "./retry";
|
||||||
import { Upload } from "./upload";
|
import { Upload } from "./upload";
|
||||||
import { client } from "./fill/client";
|
import { client } from "./fill/client";
|
||||||
import { Clipboard, clipboard } from "./fill/clipboard";
|
import { clipboard } from "./fill/clipboard";
|
||||||
import { INotificationService, NotificationService, IProgressService, ProgressService } from "./fill/notification";
|
import { INotificationService, NotificationService, IProgressService, ProgressService } from "./fill/notification";
|
||||||
import { IURIFactory } from "./fill/uri";
|
import { IURIFactory } from "./fill/uri";
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ import { IURIFactory } from "./fill/uri";
|
||||||
export abstract class Client {
|
export abstract class Client {
|
||||||
|
|
||||||
public readonly retry = retry;
|
public readonly retry = retry;
|
||||||
public readonly clipboard: Clipboard = clipboard;
|
public readonly clipboard = clipboard;
|
||||||
public readonly uriFactory: IURIFactory;
|
public readonly uriFactory: IURIFactory;
|
||||||
public readonly upload = new Upload(new NotificationService(), new ProgressService());
|
public readonly upload = new Upload(new NotificationService(), new ProgressService());
|
||||||
private start: Time | undefined;
|
private start: Time | undefined;
|
||||||
|
|
|
@ -36,6 +36,30 @@ export class Clipboard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paste currently copied text.
|
||||||
|
*/
|
||||||
|
public async paste(): Promise<boolean> {
|
||||||
|
if (this.isEnabled) {
|
||||||
|
try {
|
||||||
|
const element = document.activeElement as HTMLInputElement | HTMLTextAreaElement;
|
||||||
|
const start = element.selectionStart || 0;
|
||||||
|
const end = element.selectionEnd;
|
||||||
|
const allText = element.value;
|
||||||
|
const newText = allText.substring(0, start)
|
||||||
|
+ (await this.readText())
|
||||||
|
+ allText.substring(end || start);
|
||||||
|
element.value = newText;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (ex) {
|
||||||
|
// Will try execCommand below.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return document.execCommand("paste");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if the native clipboard is supported.
|
* Return true if the native clipboard is supported.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,7 +9,6 @@ import "./vscode.scss";
|
||||||
import { Client as IDEClient, IURI, IURIFactory, IProgress, INotificationHandle } from "@coder/ide";
|
import { Client as IDEClient, IURI, IURIFactory, IProgress, INotificationHandle } from "@coder/ide";
|
||||||
import { registerContextMenuListener } from "vs/base/parts/contextmenu/electron-main/contextmenu";
|
import { registerContextMenuListener } from "vs/base/parts/contextmenu/electron-main/contextmenu";
|
||||||
import { LogLevel } from "vs/platform/log/common/log";
|
import { LogLevel } from "vs/platform/log/common/log";
|
||||||
// import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey";
|
|
||||||
import { URI } from "vs/base/common/uri";
|
import { URI } from "vs/base/common/uri";
|
||||||
import { INotificationService } from "vs/platform/notification/common/notification";
|
import { INotificationService } from "vs/platform/notification/common/notification";
|
||||||
import { IProgressService2, ProgressLocation } from "vs/platform/progress/common/progress";
|
import { IProgressService2, ProgressLocation } from "vs/platform/progress/common/progress";
|
||||||
|
@ -19,11 +18,15 @@ import { IEditorService, IResourceEditor } from "vs/workbench/services/editor/co
|
||||||
import { IEditorGroup } from "vs/workbench/services/group/common/editorGroupsService";
|
import { IEditorGroup } from "vs/workbench/services/group/common/editorGroupsService";
|
||||||
import { IWindowsService } from "vs/platform/windows/common/windows";
|
import { IWindowsService } from "vs/platform/windows/common/windows";
|
||||||
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
|
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
|
||||||
|
import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey";
|
||||||
|
import { Action } from "vs/base/common/actions";
|
||||||
|
import * as nls from "vs/nls";
|
||||||
|
|
||||||
export class Client extends IDEClient {
|
export class Client extends IDEClient {
|
||||||
|
|
||||||
private readonly windowId = parseInt(new Date().toISOString().replace(/[-:.TZ]/g, ""), 10);
|
private readonly windowId = parseInt(new Date().toISOString().replace(/[-:.TZ]/g, ""), 10);
|
||||||
private _serviceCollection: ServiceCollection | undefined;
|
private _serviceCollection: ServiceCollection | undefined;
|
||||||
|
private _clipboardContextKey: RawContextKey<boolean> | undefined;
|
||||||
|
|
||||||
public async handleExternalDrop(target: ExplorerItem | Model, originalEvent: DragMouseEvent): Promise<void> {
|
public async handleExternalDrop(target: ExplorerItem | Model, originalEvent: DragMouseEvent): Promise<void> {
|
||||||
await this.upload.uploadDropped(
|
await this.upload.uploadDropped(
|
||||||
|
@ -57,6 +60,47 @@ export class Client extends IDEClient {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use to toggle the paste option inside editors based on the native clipboard.
|
||||||
|
*/
|
||||||
|
public get clipboardContextKey(): RawContextKey<boolean> {
|
||||||
|
if (!this._clipboardContextKey) {
|
||||||
|
throw new Error("Trying to access clipboard context key before it has been set");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._clipboardContextKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get clipboardText(): Promise<string> {
|
||||||
|
return this.clipboard.readText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a paste action for use in text inputs.
|
||||||
|
*/
|
||||||
|
public get pasteAction(): Action {
|
||||||
|
const getLabel = (enabled: boolean): string => {
|
||||||
|
return enabled
|
||||||
|
? nls.localize("paste", "Paste")
|
||||||
|
: nls.localize("pasteWithKeybind", "Paste (must use keybind)");
|
||||||
|
};
|
||||||
|
|
||||||
|
const pasteAction = new Action(
|
||||||
|
"editor.action.clipboardPasteAction",
|
||||||
|
getLabel(this.clipboard.isEnabled),
|
||||||
|
undefined,
|
||||||
|
this.clipboard.isEnabled,
|
||||||
|
async (): Promise<boolean> => this.clipboard.paste(),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.clipboard.onPermissionChange((enabled) => {
|
||||||
|
pasteAction.label = getLabel(enabled);
|
||||||
|
pasteAction.enabled = enabled;
|
||||||
|
});
|
||||||
|
|
||||||
|
return pasteAction;
|
||||||
|
}
|
||||||
|
|
||||||
public get serviceCollection(): ServiceCollection {
|
public get serviceCollection(): ServiceCollection {
|
||||||
if (!this._serviceCollection) {
|
if (!this._serviceCollection) {
|
||||||
throw new Error("Trying to access service collection before it has been set");
|
throw new Error("Trying to access service collection before it has been set");
|
||||||
|
@ -134,6 +178,8 @@ export class Client extends IDEClient {
|
||||||
process.env.VSCODE_LOGS = data.logPath;
|
process.env.VSCODE_LOGS = data.logPath;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._clipboardContextKey = new RawContextKey("nativeClipboard", this.clipboard.isEnabled);
|
||||||
|
|
||||||
return this.task("Start workbench", 1000, async (data) => {
|
return this.task("Start workbench", 1000, async (data) => {
|
||||||
paths._paths.appData = data.dataDirectory;
|
paths._paths.appData = data.dataDirectory;
|
||||||
paths._paths.defaultUserData = data.dataDirectory;
|
paths._paths.defaultUserData = data.dataDirectory;
|
||||||
|
@ -154,14 +200,11 @@ export class Client extends IDEClient {
|
||||||
folderUri: URI.file(data.workingDirectory),
|
folderUri: URI.file(data.workingDirectory),
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Set up clipboard context.
|
const contextKeys = this.serviceCollection.get(IContextKeyService) as IContextKeyService;
|
||||||
// const workbench = workbenchShell.workbench;
|
const bounded = this.clipboardContextKey.bindTo(contextKeys);
|
||||||
// const contextKeys = workbench.workbenchParams.serviceCollection.get(IContextKeyService) as IContextKeyService;
|
this.clipboard.onPermissionChange((enabled) => {
|
||||||
// const clipboardContextKey = new RawContextKey("nativeClipboard", this.clipboard.isSupported);
|
bounded.set(enabled);
|
||||||
// const bounded = clipboardContextKey.bindTo(contextKeys);
|
});
|
||||||
// this.clipboard.onPermissionChange((enabled) => {
|
|
||||||
// bounded.set(enabled);
|
|
||||||
// });
|
|
||||||
this.clipboard.initialize();
|
this.clipboard.initialize();
|
||||||
}, this.initData, pathSets);
|
}, this.initData, pathSets);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,67 @@ index 457818a975..ad45ffe58a 100644
|
||||||
}
|
}
|
||||||
+
|
+
|
||||||
+startup({ machineId: "1" });
|
+startup({ machineId: "1" });
|
||||||
|
diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts
|
||||||
|
index 5e43f1b39e..7775e3b6da 100644
|
||||||
|
--- a/src/vs/editor/contrib/clipboard/clipboard.ts
|
||||||
|
+++ b/src/vs/editor/contrib/clipboard/clipboard.ts
|
||||||
|
@@ -16,6 +16,10 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||||
|
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||||
|
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||||
|
+import { CodeEditorWidget } from "vs/editor/browser/widget/codeEditorWidget";
|
||||||
|
+import { Handler } from "vs/editor/common/editorCommon";
|
||||||
|
+import { ContextKeyExpr } from "vs/platform/contextkey/common/contextkey";
|
||||||
|
+import { client } from "../../../../../../../packages/vscode";
|
||||||
|
|
||||||
|
const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste';
|
||||||
|
|
||||||
|
@@ -26,7 +30,8 @@ const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdgeOrIE)
|
||||||
|
// Chrome incorrectly returns true for document.queryCommandSupported('paste')
|
||||||
|
// when the paste feature is available but the calling script has insufficient
|
||||||
|
// privileges to actually perform the action
|
||||||
|
-const supportsPaste = (platform.isNative || (!browser.isChrome && document.queryCommandSupported('paste')));
|
||||||
|
+// const supportsPaste = (platform.isNative || (!browser.isChrome && document.queryCommandSupported('paste')));
|
||||||
|
+const supportsPaste = true;
|
||||||
|
|
||||||
|
type ExecCommand = 'cut' | 'copy' | 'paste';
|
||||||
|
|
||||||
|
@@ -178,7 +183,7 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||||
|
id: 'editor.action.clipboardPasteAction',
|
||||||
|
label: nls.localize('actions.clipboard.pasteLabel', "Paste"),
|
||||||
|
alias: 'Paste',
|
||||||
|
- precondition: EditorContextKeys.writable,
|
||||||
|
+ precondition: ContextKeyExpr.and(EditorContextKeys.writable, client.clipboardContextKey),
|
||||||
|
kbOpts: kbOpts,
|
||||||
|
menuOpts: {
|
||||||
|
group: CLIPBOARD_CONTEXT_MENU_GROUP,
|
||||||
|
@@ -188,10 +193,25 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||||
|
menuId: MenuId.MenubarEditMenu,
|
||||||
|
group: '2_ccp',
|
||||||
|
title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"),
|
||||||
|
- order: 3
|
||||||
|
+ order: 3,
|
||||||
|
+ when: client.clipboardContextKey,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ public async run(accessor, editor: ICodeEditor): Promise<void> {
|
||||||
|
+ if (editor instanceof CodeEditorWidget) {
|
||||||
|
+ try {
|
||||||
|
+ editor.trigger('', Handler.Paste, {
|
||||||
|
+ text: await client.clipboardText,
|
||||||
|
+ });
|
||||||
|
+ } catch (ex) {
|
||||||
|
+ super.run(accessor, editor);
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ super.run(accessor, editor);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction {
|
||||||
diff --git a/src/vs/loader.js b/src/vs/loader.js
|
diff --git a/src/vs/loader.js b/src/vs/loader.js
|
||||||
index 2bf7fe37d7..81cc668f12 100644
|
index 2bf7fe37d7..81cc668f12 100644
|
||||||
--- a/src/vs/loader.js
|
--- a/src/vs/loader.js
|
||||||
|
@ -94,6 +155,27 @@ index a43d63aa51..4c6df2fcd9 100644
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts
|
||||||
|
index ea348f3a04..7c943a71c2 100644
|
||||||
|
--- a/src/vs/workbench/electron-browser/window.ts
|
||||||
|
+++ b/src/vs/workbench/electron-browser/window.ts
|
||||||
|
@@ -38,6 +38,7 @@ import { AccessibilitySupport, isRootUser, isWindows, isMacintosh } from 'vs/bas
|
||||||
|
import product from 'vs/platform/node/product';
|
||||||
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
|
import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor';
|
||||||
|
+import { client } from "../../../../../../packages/vscode";
|
||||||
|
|
||||||
|
const TextInputActions: IAction[] = [
|
||||||
|
new Action('undo', nls.localize('undo', "Undo"), null, true, () => document.execCommand('undo') && Promise.resolve(true)),
|
||||||
|
@@ -45,7 +46,7 @@ const TextInputActions: IAction[] = [
|
||||||
|
new Separator(),
|
||||||
|
new Action('editor.action.clipboardCutAction', nls.localize('cut', "Cut"), null, true, () => document.execCommand('cut') && Promise.resolve(true)),
|
||||||
|
new Action('editor.action.clipboardCopyAction', nls.localize('copy', "Copy"), null, true, () => document.execCommand('copy') && Promise.resolve(true)),
|
||||||
|
- new Action('editor.action.clipboardPasteAction', nls.localize('paste', "Paste"), null, true, () => document.execCommand('paste') && Promise.resolve(true)),
|
||||||
|
+ client.pasteAction,
|
||||||
|
new Separator(),
|
||||||
|
new Action('editor.action.selectAll', nls.localize('selectAll', "Select All"), null, true, () => document.execCommand('selectAll') && Promise.resolve(true))
|
||||||
|
];
|
||||||
diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts
|
diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts
|
||||||
index 35bc4a82b3..9cc84bdf28 100644
|
index 35bc4a82b3..9cc84bdf28 100644
|
||||||
--- a/src/vs/workbench/electron-browser/workbench.ts
|
--- a/src/vs/workbench/electron-browser/workbench.ts
|
||||||
|
|
Loading…
Reference in New Issue