From 00b96b66943b3233f28036873b3da54b26d5c03f Mon Sep 17 00:00:00 2001 From: azerr Date: Thu, 17 Nov 2022 18:55:15 +0100 Subject: [PATCH] Wrap selection in XML element Fixes #794 Signed-off-by: azerr --- package.json | 46 +++++++++++++- src/commands/clientCommandConstants.ts | 11 +++- src/commands/registerCommands.ts | 86 +++++++++++++++++++++++++- src/commands/serverCommandConstants.ts | 9 ++- 4 files changed, 145 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index a94a7f64..1d4e3f56 100644 --- a/package.json +++ b/package.json @@ -678,17 +678,61 @@ "command": "xml.restart.language.server", "title": "Restart XML Language Server", "category": "XML" + }, + { + "command": "xml.refactor.surround.with.tags", + "title": "Surround with Tags (Wrap)", + "category": "XML" + }, + { + "command": "xml.refactor.surround.with.comments", + "title": "Surround with Comments", + "category": "XML" + }, + { + "command": "xml.refactor.surround.with.cdata", + "title": "Surround with CDATA", + "category": "XML" } ], "menus": { "commandPalette": [ { "command": "xml.validation.current.file", - "when": "editorLangId == xml" + "when": "editorLangId == xml || editorLangId == dtd || editorLangId == xsl || editorLangId == svg" }, { "command": "xml.command.bind.grammar", "when": "resourceFilename =~ /xml/ && editorIsOpen" + }, + { + "command": "xml.refactor.surround.with.tags", + "when": "editorLangId == xml || editorLangId == xsl || editorLangId == svg" + }, + { + "command": "xml.refactor.surround.with.comments", + "when": "editorLangId == xml || editorLangId == xsl || editorLangId == svg" + }, + { + "command": "xml.refactor.surround.with.cdata", + "when": "editorLangId == xml || editorLangId == xsl || editorLangId == svg" + } + ], + "editor/context": [ + { + "command": "xml.refactor.surround.with.tags", + "when": "editorLangId == xml || editorLangId == xsl || editorLangId == svg", + "group": "1_xmlactions" + }, + { + "command": "xml.refactor.surround.with.comments", + "when": "editorLangId == xml || editorLangId == xsl || editorLangId == svg", + "group": "1_xmlactions" + }, + { + "command": "xml.refactor.surround.with.cdata", + "when": "editorLangId == xml || editorLangId == xsl || editorLangId == svg", + "group": "1_xmlactions" } ] }, diff --git a/src/commands/clientCommandConstants.ts b/src/commands/clientCommandConstants.ts index fc3b1bc6..7b021f85 100644 --- a/src/commands/clientCommandConstants.ts +++ b/src/commands/clientCommandConstants.ts @@ -65,4 +65,13 @@ export const EXECUTE_WORKSPACE_COMMAND = 'xml.workspace.executeCommand'; /** * Command to restart connection to language server. */ - export const RESTART_LANGUAGE_SERVER = 'xml.restart.language.server'; \ No newline at end of file + export const RESTART_LANGUAGE_SERVER = 'xml.restart.language.server'; + +/** + * Command to wrap element. + */ + export const REFACTOR_SURROUND_WITH_TAGS = 'xml.refactor.surround.with.tags'; + + export const REFACTOR_SURROUND_WITH_COMMENTS = 'xml.refactor.surround.with.comments'; + + export const REFACTOR_SURROUND_WITH_CDATA = 'xml.refactor.surround.with.cdata'; \ No newline at end of file diff --git a/src/commands/registerCommands.ts b/src/commands/registerCommands.ts index be0bf5f4..199b5eb2 100644 --- a/src/commands/registerCommands.ts +++ b/src/commands/registerCommands.ts @@ -1,6 +1,6 @@ import * as path from 'path'; -import { commands, ConfigurationTarget, env, ExtensionContext, OpenDialogOptions, Position, QuickPickItem, TextDocument, Uri, window, workspace, WorkspaceEdit } from "vscode"; -import { CancellationToken, ExecuteCommandParams, ExecuteCommandRequest, ReferencesRequest, TextDocumentEdit, TextDocumentIdentifier } from "vscode-languageclient"; +import { commands, ConfigurationTarget, env, ExtensionContext, OpenDialogOptions, Position, QuickPickItem, SnippetString, TextDocument, Uri, window, workspace, WorkspaceEdit, Selection } from "vscode"; +import { CancellationToken, ExecuteCommandParams, ExecuteCommandRequest, ReferencesRequest, TextDocumentEdit, TextDocumentIdentifier, TextEdit } from "vscode-languageclient"; import { LanguageClient } from 'vscode-languageclient/node'; import { markdownPreviewProvider } from "../markdownPreviewProvider"; import { DEBUG } from '../server/java/javaServerStarter'; @@ -29,6 +29,7 @@ export async function registerClientServerCommands(context: ExtensionContext, la registerCodeLensReferencesCommands(context, languageClient); registerValidationCommands(context); + registerRefactorCommands(context, languageClient); registerAssociationCommands(context, languageClient); registerRestartLanguageServerCommand(context, languageClient); @@ -182,7 +183,7 @@ async function grammarAssociationCommand(documentURI: Uri, languageClient: Langu if (!predefinedUrl || !predefinedUrl.startsWith('http')) { predefinedUrl = ''; } - grammarURI = await window.showInputBox({title:'Fill with schema / grammar URL' , value:predefinedUrl}); + grammarURI = await window.showInputBox({ title: 'Fill with schema / grammar URL', value: predefinedUrl }); } else { // step 2.1: Open a dialog to select the XSD, DTD, RelaxNG file to bind. const options: OpenDialogOptions = { @@ -366,3 +367,82 @@ function registerRestartLanguageServerCommand(context: ExtensionContext, languag })); } + +interface SurroundWithResponse { + start: TextEdit; + end: TextEdit; +} + +class SurroundWithKind { + + static readonly tags= 'tags'; + static readonly comments= 'comments'; + static readonly cdata= 'cdata'; + +} + +/** + * Register commands used for refactoring XML files + * + * @param context the extension context + */ +function registerRefactorCommands(context: ExtensionContext, languageClient: LanguageClient) { + + // Surround with Tags (Wrap) + context.subscriptions.push(commands.registerCommand(ClientCommandConstants.REFACTOR_SURROUND_WITH_TAGS, async () => { + await surroundWith(SurroundWithKind.tags, languageClient); + })); + + // Surround with Comments + context.subscriptions.push(commands.registerCommand(ClientCommandConstants.REFACTOR_SURROUND_WITH_COMMENTS, async () => { + await surroundWith(SurroundWithKind.comments, languageClient); + })); + + + // Surround with CDATA + context.subscriptions.push(commands.registerCommand(ClientCommandConstants.REFACTOR_SURROUND_WITH_CDATA, async () => { + await surroundWith(SurroundWithKind.cdata, languageClient); + })); +} + +async function surroundWith(surroundWithType: SurroundWithKind, languageClient: LanguageClient) { + const editor = window.activeTextEditor; + if (!editor) { + return; + } + const selection = editor.selections[0]; + if (!selection) { + return; + } + + const uri = window.activeTextEditor.document.uri; + const identifier = TextDocumentIdentifier.create(uri.toString()); + const range = languageClient.code2ProtocolConverter.asRange(selection); + // Set the custom condition to watch if file already has bound grammar + let result: SurroundWithResponse; + try { + result = await commands.executeCommand(ServerCommandConstants.REFACTOR_SURROUND_WITH, identifier, range, surroundWithType); + } catch (error) { + console.log(`Error while surround with : ${error}`); + } + + const startTag = result.start.newText; + const endTag = result.end.newText; + const startPos = languageClient.protocol2CodeConverter.asPosition(result.start.range.start); + const endPos = languageClient.protocol2CodeConverter.asPosition(result.end.range.start); + const pos = new Position(selection.start.line, selection.start.character + 1); + + editor.insertSnippet(new SnippetString(endTag), endPos); + editor.insertSnippet(new SnippetString(startTag), startPos); + //editor.edit((selectedText) => { + // selectedText.insert(startPos, startTag); + // selectedText.insert(endPos, endTag); + //}) + + const s = new Selection(pos, pos); + editor.selections = [s]; + + if (surroundWithType === SurroundWithKind.tags) { + commands.executeCommand("editor.action.triggerSuggest"); + } +} diff --git a/src/commands/serverCommandConstants.ts b/src/commands/serverCommandConstants.ts index 7aab9dab..8d2eda44 100644 --- a/src/commands/serverCommandConstants.ts +++ b/src/commands/serverCommandConstants.ts @@ -24,9 +24,14 @@ export const ASSOCIATE_GRAMMAR_INSERT = "xml.associate.grammar.insert"; /** * Command to check if the current XML document is bound to a grammar */ -export const CHECK_BOUND_GRAMMAR = "xml.check.bound.grammar" +export const CHECK_BOUND_GRAMMAR = "xml.check.bound.grammar"; /** * Command to check if a given file pattern matches any file on the workspace */ -export const CHECK_FILE_PATTERN = "xml.check.file.pattern" \ No newline at end of file +export const CHECK_FILE_PATTERN = "xml.check.file.pattern"; + +/** + * Command to ... + */ + export const REFACTOR_SURROUND_WITH = "xml.refactor.surround.with"; \ No newline at end of file