Skip to content

Commit

Permalink
feat: [WIP] convert Maven XML dependencies on paste
Browse files Browse the repository at this point in the history
Signed-off-by: Fred Bricon <fbricon@gmail.com>
  • Loading branch information
fbricon committed Feb 12, 2023
1 parent c61881d commit 8ed9ec7
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 26 deletions.
42 changes: 21 additions & 21 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@
"url": "https://github.com/jbangdev/jbang-vscode"
},
"engines": {
"vscode": "^1.65.0"
"vscode": "^1.70.0"
},
"enabledApiProposals": [
"documentPaste"
],
"categories": [
"Other"
],
Expand Down Expand Up @@ -235,7 +238,7 @@
"test": "node ./out/test/runTest.js"
},
"devDependencies": {
"@types/vscode": "1.65.0",
"@types/vscode": "1.70.0",
"@types/glob": "7.2.0",
"@types/mocha": "9.1.1",
"@types/node": "14.18.33",
Expand All @@ -253,6 +256,6 @@
"axios": "0.27.2",
"fast-xml-parser": "^4.0.10",
"lru-cache": "^7.14.0",
"vscode-languageclient": "^8.0.1"
"vscode-languageclient": "^8.0.2"
}
}
}
94 changes: 94 additions & 0 deletions src/DependencyPasteEventHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { XMLParser } from "fast-xml-parser";
import { CancellationToken, DataTransfer, DocumentPasteEdit, DocumentPasteEditProvider, DocumentPasteProviderMetadata, ExtensionContext, languages, Range, TextDocument } from "vscode";
import { isJBangFile, SUPPORTED_LANGUAGES } from "./JBangUtils";

const TEXT_MIMETYPE: string = "text/plain";
const MIMETYPES: DocumentPasteProviderMetadata = {
pasteMimeTypes: [TEXT_MIMETYPE]
};


export class DependencyPasteEditProvider implements DocumentPasteEditProvider {

/**
* `DocumentPasteEditProvider` that delegates to jdt.ls to make any changes necessary to the pasted text and add any additional workspace edits.
*/

async provideDocumentPasteEdits(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined> {
if (!isJBangFile(document.getText())){
return undefined;
}
const pasteContent = dataTransfer.get(TEXT_MIMETYPE);
if (!pasteContent) {
return undefined;
}
const pastedText: string = (await pasteContent.asString()).trim();

// don't try to provide for multi character inserts; the implementation will get messy and the feature won't be that helpful
if (!pastedText || token.isCancellationRequested || ranges.length !== 1) {
return undefined;
}

const range = ranges[0];
if (range.start.character > 0) {
//Only paste on the 1st column, for now
return undefined;
}
const line = range.start.line;
const targetLine = document.lineAt(line);
if (!targetLine.isEmptyOrWhitespace) {
return undefined;
}
let dependencies:any;
let xml: any;
if (pastedText.startsWith("<dependency>")
|| pastedText.startsWith("<dependencies>")
|| pastedText.startsWith("<dependencyManagement>")) {
xml = new XMLParser().parse(pastedText);
if (xml.dependency) {
dependencies = xml.dependency;
} else if (xml.dependencies?.dependency) {
dependencies = xml.dependencies.dependency;
} else if (xml.dependencyManagement?.dependencies?.dependency) {
dependencies = xml.dependencyManagement.dependencies.dependency;
}
}

if (!dependencies) {
return undefined;
}
if (Array.isArray(dependencies)){
return new DocumentPasteEdit(dependencies.map(d => this.toJBangDependency(d)).join('\n'));
} else {
return new DocumentPasteEdit(this.toJBangDependency(dependencies));
}

// either the handler returns null or encounters problems, fall back to return undefined to let VS Code ignore this handler
return undefined;
}


private toJBangDependency(dependency: any): string {
const suffix = dependency?.type === 'pom'? '@pom':'';
const version = dependency?.version ? dependency.version: "LATEST";
return `//DEPS ${dependency.groupId}:${dependency.artifactId}:${version}${suffix}`;
}

/**
* Registers the DependencyPasteEditProvider and sets it up to be disposed.
*
* @param context the extension context
*/
public initialize(context: ExtensionContext) {
if (languages.registerDocumentPasteEditProvider) {
const dependencyPasteEditProvider = new DependencyPasteEditProvider();
SUPPORTED_LANGUAGES.forEach(languageId => {
context.subscriptions.push(
languages.registerDocumentPasteEditProvider(languageId, dependencyPasteEditProvider, MIMETYPES)
);
});
}
}
}

export default new DependencyPasteEditProvider();
3 changes: 2 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Assets } from "./Assets";
import CodeLensProvider from "./CodeLensProvider";
import CommandManager from "./CommandManager";
import JBangCompletionProvider from "./CompletionProvider";
import DependencyPasteEventProvider from "./DependencyPasteEventHandler";
import EditorListener from "./EditorListener";
import JBangConfig from "./JBangConfig";
import JBangDebugger from "./JBangDebugger";
Expand All @@ -22,7 +23,7 @@ export function activate(context: ExtensionContext) {
CodeLensProvider.initialize(context);
JBangCompletionProvider.initialize(context);
JBangHoverProvider.initialize(context);
DependencyPasteEventProvider.initialize(context);
console.log(`${context.extension.packageJSON.name} ${version} is now active!`);
}

export function deactivate() {}
75 changes: 75 additions & 0 deletions vscode.proposed.documentPaste.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

declare module 'vscode' {

// https://github.com/microsoft/vscode/issues/30066/

/**
* Provider invoked when the user copies and pastes code.
*/
interface DocumentPasteEditProvider {

/**
* Optional method invoked after the user copies text in a file.
*
* During {@link prepareDocumentPaste}, an extension can compute metadata that is attached to
* a {@link DataTransfer} and is passed back to the provider in {@link provideDocumentPasteEdits}.
*
* @param document Document where the copy took place.
* @param ranges Ranges being copied in the `document`.
* @param dataTransfer The data transfer associated with the copy. You can store additional values on this for later use in {@link provideDocumentPasteEdits}.
* @param token A cancellation token.
*/
prepareDocumentPaste?(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): void | Thenable<void>;

/**
* Invoked before the user pastes into a document.
*
* In this method, extensions can return a workspace edit that replaces the standard pasting behavior.
*
* @param document Document being pasted into
* @param ranges Currently selected ranges in the document.
* @param dataTransfer The data transfer associated with the paste.
* @param token A cancellation token.
*
* @return Optional workspace edit that applies the paste. Return undefined to use standard pasting.
*/
provideDocumentPasteEdits(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): ProviderResult<DocumentPasteEdit>;
}

/**
* An operation applied on paste
*/
class DocumentPasteEdit {
/**
* The text or snippet to insert at the pasted locations.
*/
insertText: string | SnippetString;

/**
* An optional additional edit to apply on paste.
*/
additionalEdit?: WorkspaceEdit;

/**
* @param insertText The text or snippet to insert at the pasted locations.
*/
constructor(insertText: string | SnippetString);
}

interface DocumentPasteProviderMetadata {
/**
* Mime types that `provideDocumentPasteEdits` should be invoked for.
*
* Use the special `files` mimetype to indicate the provider should be invoked if any files are present in the `DataTransfer`.
*/
readonly pasteMimeTypes: readonly string[];
}

namespace languages {
export function registerDocumentPasteEditProvider(selector: DocumentSelector, provider: DocumentPasteEditProvider, metadata: DocumentPasteProviderMetadata): Disposable;
}
}

0 comments on commit 8ed9ec7

Please sign in to comment.