diff --git a/package.json b/package.json index 625f4eddc7..4117101e4b 100644 --- a/package.json +++ b/package.json @@ -1487,6 +1487,11 @@ "command": "java.server.restart", "title": "%java.server.restart%", "category": "Java" + }, + { + "command": "java.action.filesExplorerPasteAction", + "title": "%java.action.filesExplorerPasteAction%", + "category": "Java" } ], "keybindings": [ @@ -1504,6 +1509,12 @@ "key": "ctrl+shift+v", "mac": "cmd+shift+v", "when": "javaLSReady && editorLangId == java" + }, + { + "command": "java.action.filesExplorerPasteAction", + "key": "ctrl+shift+v", + "mac": "cmd+shift+v", + "when": "explorerViewletFocus && config.editor.pasteAs.enabled" } ], "menus": { @@ -1622,6 +1633,10 @@ { "command": "java.server.restart", "when": "javaLSReady" + }, + { + "command": "java.action.filesExplorerPasteAction", + "when": "false" } ], "view/title": [ diff --git a/package.nls.json b/package.nls.json index e01775313b..ca4690b3bb 100644 --- a/package.nls.json +++ b/package.nls.json @@ -25,5 +25,6 @@ "java.project.createModuleInfo.command": "Create module-info.java", "java.clean.sharedIndexes": "Clean Shared Indexes", "java.server.restart": "Restart Java Language Server", - "java.edit.smartSemicolonDetection": "Java Smart Semicolon Detection" + "java.edit.smartSemicolonDetection": "Java Smart Semicolon Detection", + "java.action.filesExplorerPasteAction": "Paste clipboard text into a file" } diff --git a/src/commands.ts b/src/commands.ts index 0654ec3635..5ad0a8085a 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -191,6 +191,10 @@ export namespace Commands { * Custom paste action (triggers auto-import) */ export const CLIPBOARD_ONPASTE = 'java.action.clipboardPasteAction'; + /** + * Custom paste action in files explorer + */ + export const FILESEXPLORER_ONPASTE = 'java.action.filesExplorerPasteAction'; /** * Choose type to import. */ @@ -341,4 +345,9 @@ export namespace Commands { */ export const SMARTSEMICOLON_DETECTION = "java.edit.smartSemicolonDetection"; + /** + * Determine if pasted text is a java file and resolve packages + */ + export const RESOLVE_PASTED_TEXT = "java.project.resolveText"; + } diff --git a/src/extension.ts b/src/extension.ts index 8f519e7967..18eb617a97 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -37,6 +37,7 @@ import { TelemetryService } from '@redhat-developer/vscode-redhat-telemetry/lib' import { activationProgressNotification } from "./serverTaskPresenter"; import { loadSupportedJreNames } from './jdkUtils'; import { BuildFileSelector, PICKED_BUILD_FILES, cleanupProjectPickerCache } from './buildFilesSelector'; +import { pasteFile } from './pasteAction'; const syntaxClient: SyntaxLanguageClient = new SyntaxLanguageClient(); const standardClient: StandardLanguageClient = new StandardLanguageClient(); @@ -93,6 +94,14 @@ function getHeapDumpFolderFromSettings(): string { export async function activate(context: ExtensionContext): Promise { await loadSupportedJreNames(context); + context.subscriptions.push(commands.registerCommand(Commands.FILESEXPLORER_ONPASTE, async () => { + const originalClipboard = await env.clipboard.readText(); + // Hack in order to get path to selected folder if applicable (see https://github.com/microsoft/vscode/issues/3553#issuecomment-1098562676) + await commands.executeCommand('copyFilePath'); + const folder = await env.clipboard.readText(); + await env.clipboard.writeText(originalClipboard); + pasteFile(folder); + })); context.subscriptions.push(markdownPreviewProvider); context.subscriptions.push(commands.registerCommand(Commands.TEMPLATE_VARIABLES, async () => { markdownPreviewProvider.show(context.asAbsolutePath(path.join('document', `${Commands.TEMPLATE_VARIABLES}.md`)), 'Predefined Variables', "", context); @@ -652,7 +661,7 @@ function enableJavadocSymbols() { }); } -function getTempWorkspace() { +export function getTempWorkspace() { return path.resolve(os.tmpdir(), `vscodesws_${makeRandomHexString(5)}`); } diff --git a/src/pasteAction.ts b/src/pasteAction.ts index c500f0f3eb..7b46c8a095 100644 --- a/src/pasteAction.ts +++ b/src/pasteAction.ts @@ -1,10 +1,16 @@ 'use strict'; -import { commands, env, ExtensionContext, Range, TextEditor, window } from 'vscode'; +import { commands, env, ExtensionContext, Range, TextEditor, Uri, window, workspace } from 'vscode'; import { LanguageClient } from 'vscode-languageclient/node'; import { Commands } from './commands'; +import fs = require('fs'); +const os = require('os'); +import { apiManager } from './apiManager'; +import { TextEncoder } from 'util'; +import { getTempWorkspace } from './extension'; + export function registerCommands(languageClient: LanguageClient, context: ExtensionContext) { context.subscriptions.push(commands.registerCommand(Commands.CLIPBOARD_ONPASTE, () => { registerOrganizeImportsOnPasteCommand(); @@ -48,4 +54,32 @@ export async function registerOrganizeImportsOnPasteCommand(): Promise { } } }); +} + +let serverReady = false; + +export async function pasteFile(folder: fs.PathLike): Promise { + const clipboardText: string = await env.clipboard.readText(); + let filePath = folder.toString(); + fs.stat(folder, async (err, stats) => { + // If given path to selected folder is invalid (no folder is selected) + if (filePath === clipboardText || stats.isFile() || (filePath === "." && workspace.workspaceFolders !== undefined)) { + filePath = workspace.workspaceFolders[0].uri.path; + } + if (!serverReady) { + await apiManager.getApiInstance().serverReady().then( async () => { + serverReady = true; + }); + } + const fileString: string = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.RESOLVE_PASTED_TEXT, filePath, clipboardText); + const fileUri = fileString !== null ? Uri.file(fileString) : null; + if (fileUri !== null){ + try { + await workspace.fs.writeFile(fileUri, new TextEncoder().encode(clipboardText)); + window.showTextDocument(fileUri, { preview: false }); + } catch (error: unknown) { + // Do nothing (file does not have write permissions) + } + } + }); } \ No newline at end of file diff --git a/test/lightweight-mode-suite/extension.test.ts b/test/lightweight-mode-suite/extension.test.ts index 495e906244..b3424da6c6 100644 --- a/test/lightweight-mode-suite/extension.test.ts +++ b/test/lightweight-mode-suite/extension.test.ts @@ -25,6 +25,7 @@ suite('Java Language Extension - LightWeight', () => { Commands.OPEN_FILE, Commands.CLEAN_SHARED_INDEXES, Commands.RESTART_LANGUAGE_SERVER, + Commands.FILESEXPLORER_ONPASTE ].sort(); const foundJavaCommands = commands.filter((value) => { return JAVA_COMMANDS.indexOf(value)>=0 || value.startsWith('java.'); diff --git a/test/standard-mode-suite/extension.test.ts b/test/standard-mode-suite/extension.test.ts index 3fd5828b71..c364990e82 100644 --- a/test/standard-mode-suite/extension.test.ts +++ b/test/standard-mode-suite/extension.test.ts @@ -118,6 +118,8 @@ suite('Java Language Extension - Standard', () => { Commands.UPDATE_SOURCE_ATTACHMENT_CMD, Commands.SMARTSEMICOLON_DETECTION, Commands.RESOLVE_SOURCE_ATTACHMENT, + Commands.FILESEXPLORER_ONPASTE, + Commands.RESOLVE_PASTED_TEXT, ].sort(); const foundJavaCommands = commands.filter((value) => { return JAVA_COMMANDS.indexOf(value)>=0 || value.startsWith('java.');