Skip to content

Commit f93cc8b

Browse files
datho7561rgrunber
authored andcommitted
Delegate paste event handling to jdt.ls
- Invoke jdt.ls's `java.edit.handlePasteEvent` command when vscode-java receives a paste event Requires eclipse-jdtls/eclipse.jdt.ls#2349 Closes #1249 Signed-off-by: David Thompson <[email protected]>
1 parent 811ecd1 commit f93cc8b

File tree

7 files changed

+276
-39
lines changed

7 files changed

+276
-39
lines changed

.eslintignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# .gitignore
2+
out
3+
server
4+
node_modules
5+
*.vsix
6+
.DS_Store
7+
.vscode-test
8+
undefined
9+
target
10+
dist
11+
jre
12+
lombok
13+
bin/
14+
.settings
15+
.classpath
16+
.project
17+
test/resources/projects/**/.vscode
18+
test/resources/projects/maven/salut/testGradle
19+
test-temp
20+
21+
# specific to eslint
22+
vscode*.d.ts

package-lock.json

Lines changed: 32 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
"publisher": "redhat",
1010
"bugs": "https://github.com/redhat-developer/vscode-java/issues",
1111
"preview": false,
12-
"enableProposedApi": false,
12+
"enabledApiProposals": [
13+
"documentPaste"
14+
],
1315
"capabilities": {
1416
"untrustedWorkspaces": {
1517
"supported": "limited",
@@ -1345,7 +1347,7 @@
13451347
"build-server": "./node_modules/.bin/gulp build_server",
13461348
"fast-build-server": "./node_modules/.bin/gulp dev_server",
13471349
"watch-server": "./node_modules/.bin/gulp watch_server",
1348-
"eslint": "eslint --ignore-path .gitignore --ext .js,.ts ."
1350+
"eslint": "eslint --ignore-path .eslintignore --ext .js,.ts ."
13491351
},
13501352
"devDependencies": {
13511353
"@types/fs-extra": "^8.0.0",

src/commands.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ export namespace Commands {
182182
* Organize imports silently.
183183
*/
184184
export const ORGANIZE_IMPORTS_SILENTLY = "java.edit.organizeImports";
185+
/**
186+
* Handle a paste event.
187+
*/
188+
export const HANDLE_PASTE_EVENT = "java.edit.handlePasteEvent";
185189
/**
186190
* Custom paste action (triggers auto-import)
187191
*/

src/pasteEventHandler.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { CancellationToken, commands, DataTransfer, DocumentPasteEdit as VDocumentPasteEdit, DocumentPasteEditProvider, DocumentPasteProviderMetadata, ExtensionContext, languages, Range, TextDocument, window } from "vscode";
2+
import { FormattingOptions, Location, WorkspaceEdit as PWorkspaceEdit } from "vscode-languageclient";
3+
import { LanguageClient } from "vscode-languageclient/node";
4+
import { Commands } from "./commands";
5+
import { JAVA_SELECTOR } from "./standardLanguageClient";
6+
7+
const TEXT_MIMETYPE: string = "text/plain";
8+
const MIMETYPES: DocumentPasteProviderMetadata = {
9+
pasteMimeTypes: [TEXT_MIMETYPE]
10+
};
11+
12+
/**
13+
* Parameters for `Commands.HANDLE_PASTE_EVENT`
14+
*/
15+
interface PasteEventParams {
16+
location: Location;
17+
text: string;
18+
formattingOptions: FormattingOptions;
19+
copiedDocumentUri?: string;
20+
}
21+
22+
/**
23+
* Response from jdt.ls for `Commands.HANDLE_PASTE_EVENT` that's similar, but not identical, to VS Code's paste edit.
24+
*
25+
* @see VDocumentPasteEdit
26+
*/
27+
interface PDocumentPasteEdit {
28+
insertText: string;
29+
additionalEdit: PWorkspaceEdit;
30+
}
31+
32+
/**
33+
* Registers the vscode-java DocumentPasteEditProviders and sets them up to be disposed.
34+
*
35+
* @param context the extension context
36+
*/
37+
export function registerPasteEventHandler(context: ExtensionContext, languageClient: LanguageClient) {
38+
if (languages["registerDocumentPasteEditProvider"]) {
39+
context.subscriptions.push(languages["registerDocumentPasteEditProvider"](JAVA_SELECTOR, new PasteEditProvider(languageClient), MIMETYPES));
40+
}
41+
}
42+
43+
/**
44+
* `DocumentPasteEditProvider` that delegates to jdt.ls to make any changes necessary to the pasted text and add any additional workspace edits.
45+
*/
46+
class PasteEditProvider implements DocumentPasteEditProvider {
47+
48+
private languageClient: LanguageClient;
49+
private copiedContent: string | undefined;
50+
private copiedDocumentUri: string | undefined;
51+
52+
constructor(languageClient: LanguageClient) {
53+
this.languageClient = languageClient;
54+
}
55+
56+
async prepareDocumentPaste?(document: TextDocument, _ranges: readonly Range[], dataTransfer: DataTransfer, _token: CancellationToken): Promise<void> {
57+
const copiedContent: string = await dataTransfer.get(TEXT_MIMETYPE).asString();
58+
if (copiedContent) {
59+
this.copiedDocumentUri = document.uri.toString();
60+
this.copiedContent = copiedContent;
61+
}
62+
}
63+
64+
async provideDocumentPasteEdits(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): Promise<VDocumentPasteEdit> {
65+
66+
const insertText: string = await dataTransfer.get(TEXT_MIMETYPE).asString();
67+
68+
// don't try to provide for multi character inserts; the implementation will get messy and the feature won't be that helpful
69+
if (!insertText || token.isCancellationRequested || ranges.length !== 1) {
70+
return null;
71+
}
72+
73+
const range = ranges[0];
74+
75+
const location: Location = {
76+
range: this.languageClient.code2ProtocolConverter.asRange(range),
77+
uri: document.uri.toString(),
78+
};
79+
80+
const activeTextEditor = window.activeTextEditor;
81+
82+
const pasteEventParams: PasteEventParams = {
83+
location: location,
84+
text: insertText,
85+
copiedDocumentUri: this.copiedContent === insertText ? this.copiedDocumentUri : undefined,
86+
formattingOptions: {
87+
insertSpaces: <boolean>activeTextEditor.options.insertSpaces,
88+
tabSize: <number>activeTextEditor.options.tabSize
89+
}
90+
};
91+
92+
const pasteResponse: PDocumentPasteEdit = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.HANDLE_PASTE_EVENT, JSON.stringify(pasteEventParams));
93+
94+
if (pasteResponse) {
95+
return {
96+
insertText: pasteResponse.insertText,
97+
additionalEdit: pasteResponse.additionalEdit ? this.languageClient.protocol2CodeConverter.asWorkspaceEdit(pasteResponse.additionalEdit) : undefined
98+
} as VDocumentPasteEdit;
99+
}
100+
}
101+
102+
}

src/standardLanguageClient.ts

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,42 @@
11
'use strict';
22

3-
import { ExtensionContext, window, workspace, commands, Uri, ProgressLocation, ViewColumn, EventEmitter, extensions, Location, languages, CodeActionKind, TextEditor, CancellationToken, ConfigurationTarget } from "vscode";
4-
import { Commands } from "./commands";
5-
import { serverStatus, ServerStatusKind } from "./serverStatus";
6-
import { prepareExecutable, awaitServerConnection } from "./javaServerStarter";
7-
import { LanguageClientOptions, Position as LSPosition, Location as LSLocation, MessageType, TextDocumentPositionParams, ConfigurationRequest, ConfigurationParams } from "vscode-languageclient";
8-
import { LanguageClient, StreamInfo } from "vscode-languageclient/node";
9-
import { CompileWorkspaceRequest, CompileWorkspaceStatus, SourceAttachmentRequest, SourceAttachmentResult, SourceAttachmentAttribute, FeatureStatus, StatusNotification, ProgressReportNotification, ActionableNotification, ExecuteClientCommandRequest, ServerNotification, EventNotification, EventType, LinkLocation, FindLinks, GradleCompatibilityInfo, UpgradeGradleWrapperInfo, BuildProjectRequest, BuildProjectParams } from "./protocol";
10-
import { setGradleWrapperChecksum, excludeProjectSettingsFiles, ServerMode } from "./settings";
11-
import { onExtensionChange, collectBuildFilePattern } from "./plugin";
12-
import { activationProgressNotification, serverTaskPresenter } from "./serverTaskPresenter";
13-
import { getJdkUrl, RequirementsData, sortJdksBySource, sortJdksByVersion } from "./requirements";
14-
import * as net from 'net';
153
import * as fse from 'fs-extra';
4+
import { findRuntimes } from "jdk-utils";
5+
import * as net from 'net';
166
import * as path from 'path';
17-
import { getAllJavaProjects, getJavaConfig, getJavaConfiguration } from "./utils";
18-
import { logger } from "./log";
19-
import * as buildPath from './buildpath';
20-
import * as sourceAction from './sourceAction';
21-
import * as refactorAction from './refactorAction';
22-
import * as pasteAction from './pasteAction';
23-
import { serverTasks } from "./serverTasks";
7+
import { CancellationToken, CodeActionKind, commands, ConfigurationTarget, DocumentSelector, EventEmitter, ExtensionContext, extensions, languages, Location, ProgressLocation, TextEditor, Uri, ViewColumn, window, workspace } from "vscode";
8+
import { ConfigurationParams, ConfigurationRequest, LanguageClientOptions, Location as LSLocation, MessageType, Position as LSPosition, TextDocumentPositionParams } from "vscode-languageclient";
9+
import { LanguageClient, StreamInfo } from "vscode-languageclient/node";
2410
import { apiManager } from "./apiManager";
25-
import { ExtensionAPI, ClientStatus } from "./extension.api";
26-
import { serverStatusBarProvider } from "./serverStatusBarProvider";
11+
import * as buildPath from './buildpath';
12+
import { javaRefactorKinds, RefactorDocumentProvider } from "./codeActionProvider";
13+
import { Commands } from "./commands";
14+
import { ClientStatus, ExtensionAPI } from "./extension.api";
2715
import * as fileEventHandler from './fileEventHandler';
16+
import { gradleCodeActionMetadata, GradleCodeActionProvider } from "./gradle/gradleCodeActionProvider";
17+
import { JavaInlayHintsProvider } from "./inlayHintsProvider";
18+
import { awaitServerConnection, prepareExecutable } from "./javaServerStarter";
19+
import { logger } from "./log";
20+
import { checkLombokDependency } from "./lombokSupport";
2821
import { markdownPreviewProvider } from "./markdownPreviewProvider";
29-
import { RefactorDocumentProvider, javaRefactorKinds } from "./codeActionProvider";
30-
import { typeHierarchyTree } from "./typeHierarchy/typeHierarchyTree";
31-
import { TypeHierarchyDirection, TypeHierarchyItem } from "./typeHierarchy/protocol";
22+
import * as pasteAction from './pasteAction';
23+
import { registerPasteEventHandler } from './pasteEventHandler';
24+
import { collectBuildFilePattern, onExtensionChange } from "./plugin";
3225
import { pomCodeActionMetadata, PomCodeActionProvider } from "./pom/pomCodeActionProvider";
33-
import { findRuntimes } from "jdk-utils";
26+
import { ActionableNotification, BuildProjectParams, BuildProjectRequest, CompileWorkspaceRequest, CompileWorkspaceStatus, EventNotification, EventType, ExecuteClientCommandRequest, FeatureStatus, FindLinks, GradleCompatibilityInfo, LinkLocation, ProgressReportNotification, ServerNotification, SourceAttachmentAttribute, SourceAttachmentRequest, SourceAttachmentResult, StatusNotification, UpgradeGradleWrapperInfo } from "./protocol";
27+
import * as refactorAction from './refactorAction';
28+
import { getJdkUrl, RequirementsData, sortJdksBySource, sortJdksByVersion } from "./requirements";
29+
import { serverStatus, ServerStatusKind } from "./serverStatus";
30+
import { serverStatusBarProvider } from "./serverStatusBarProvider";
31+
import { activationProgressNotification, serverTaskPresenter } from "./serverTaskPresenter";
32+
import { serverTasks } from "./serverTasks";
33+
import { excludeProjectSettingsFiles, ServerMode, setGradleWrapperChecksum } from "./settings";
3434
import { snippetCompletionProvider } from "./snippetCompletionProvider";
35-
import { JavaInlayHintsProvider } from "./inlayHintsProvider";
36-
import { gradleCodeActionMetadata, GradleCodeActionProvider } from "./gradle/gradleCodeActionProvider";
37-
import { checkLombokDependency } from "./lombokSupport";
35+
import * as sourceAction from './sourceAction';
3836
import { askForProjects, projectConfigurationUpdate, upgradeGradle } from "./standardLanguageClientUtils";
37+
import { TypeHierarchyDirection, TypeHierarchyItem } from "./typeHierarchy/protocol";
38+
import { typeHierarchyTree } from "./typeHierarchy/typeHierarchyTree";
39+
import { getAllJavaProjects, getJavaConfig, getJavaConfiguration } from "./utils";
3940

4041
const extensionName = 'Language Support for Java';
4142
const GRADLE_CHECKSUM = "gradle/checksum/prompt";
@@ -44,6 +45,11 @@ const USE_JAVA = "Use Java ";
4445
const AS_GRADLE_JVM = " as Gradle JVM";
4546
const UPGRADE_GRADLE = "Upgrade Gradle to ";
4647
const GRADLE_IMPORT_JVM = "java.import.gradle.java.home";
48+
export const JAVA_SELECTOR: DocumentSelector = [
49+
{ scheme: "file", language: "java", pattern: "**/*.java" },
50+
{ scheme: "jdt", language: "java", pattern: "**/*.class" },
51+
{ scheme: "untitled", language: "java", pattern: "**/*.java" }
52+
];
4753

4854
export class StandardLanguageClient {
4955

@@ -588,12 +594,10 @@ export class StandardLanguageClient {
588594
}, new GradleCodeActionProvider(context), gradleCodeActionMetadata);
589595

590596
if (languages.registerInlayHintsProvider) {
591-
context.subscriptions.push(languages.registerInlayHintsProvider([
592-
{ scheme: "file", language: "java", pattern: "**/*.java" },
593-
{ scheme: "jdt", language: "java", pattern: "**/*.class" },
594-
{ scheme: "untitled", language: "java", pattern: "**/*.java" }
595-
], new JavaInlayHintsProvider(this.languageClient)));
597+
context.subscriptions.push(languages.registerInlayHintsProvider(JAVA_SELECTOR, new JavaInlayHintsProvider(this.languageClient)));
596598
}
599+
600+
registerPasteEventHandler(context, this.languageClient);
597601
});
598602
}
599603

0 commit comments

Comments
 (0)