|
1 | 1 | import * as path from 'path'; |
2 | | -import { commands, ConfigurationTarget, env, ExtensionContext, OpenDialogOptions, Position, QuickPickItem, TextDocument, Uri, window, workspace, WorkspaceEdit } from "vscode"; |
3 | | -import { CancellationToken, ExecuteCommandParams, ExecuteCommandRequest, ReferencesRequest, TextDocumentEdit, TextDocumentIdentifier } from "vscode-languageclient"; |
| 2 | +import * as vscode from 'vscode'; |
| 3 | +import { commands, ConfigurationTarget, env, ExtensionContext, OpenDialogOptions, Position, QuickPickItem, SnippetString, TextDocument, Uri, window, workspace, WorkspaceEdit, Selection } from "vscode"; |
| 4 | +import { CancellationToken, ExecuteCommandParams, ExecuteCommandRequest, ReferencesRequest, TextDocumentEdit, TextDocumentIdentifier, TextEdit } from "vscode-languageclient"; |
4 | 5 | import { LanguageClient } from 'vscode-languageclient/node'; |
5 | 6 | import { markdownPreviewProvider } from "../markdownPreviewProvider"; |
6 | 7 | import { DEBUG } from '../server/java/javaServerStarter'; |
@@ -29,6 +30,7 @@ export async function registerClientServerCommands(context: ExtensionContext, la |
29 | 30 |
|
30 | 31 | registerCodeLensReferencesCommands(context, languageClient); |
31 | 32 | registerValidationCommands(context); |
| 33 | + registerRefactorCommands(context, languageClient); |
32 | 34 | registerAssociationCommands(context, languageClient); |
33 | 35 | registerRestartLanguageServerCommand(context, languageClient); |
34 | 36 |
|
@@ -182,7 +184,7 @@ async function grammarAssociationCommand(documentURI: Uri, languageClient: Langu |
182 | 184 | if (!predefinedUrl || !predefinedUrl.startsWith('http')) { |
183 | 185 | predefinedUrl = ''; |
184 | 186 | } |
185 | | - grammarURI = await window.showInputBox({title:'Fill with schema / grammar URL' , value:predefinedUrl}); |
| 187 | + grammarURI = await window.showInputBox({ title: 'Fill with schema / grammar URL', value: predefinedUrl }); |
186 | 188 | } else { |
187 | 189 | // step 2.1: Open a dialog to select the XSD, DTD, RelaxNG file to bind. |
188 | 190 | const options: OpenDialogOptions = { |
@@ -366,3 +368,97 @@ function registerRestartLanguageServerCommand(context: ExtensionContext, languag |
366 | 368 |
|
367 | 369 | })); |
368 | 370 | } |
| 371 | + |
| 372 | +interface SurroundWithResponse { |
| 373 | + start: TextEdit; |
| 374 | + end: TextEdit; |
| 375 | +} |
| 376 | + |
| 377 | +class SurroundWithKind { |
| 378 | + |
| 379 | + static readonly tags = 'tags'; |
| 380 | + static readonly comments = 'comments'; |
| 381 | + static readonly cdata = 'cdata'; |
| 382 | + |
| 383 | +} |
| 384 | + |
| 385 | +/** |
| 386 | + * Register commands used for refactoring XML files |
| 387 | + * |
| 388 | + * @param context the extension context |
| 389 | + */ |
| 390 | +function registerRefactorCommands(context: ExtensionContext, languageClient: LanguageClient) { |
| 391 | + |
| 392 | + // Surround with Tags (Wrap) |
| 393 | + context.subscriptions.push(commands.registerCommand(ClientCommandConstants.REFACTOR_SURROUND_WITH_TAGS, async () => { |
| 394 | + await surroundWith(SurroundWithKind.tags, languageClient); |
| 395 | + })); |
| 396 | + |
| 397 | + // Surround with Comments |
| 398 | + context.subscriptions.push(commands.registerCommand(ClientCommandConstants.REFACTOR_SURROUND_WITH_COMMENTS, async () => { |
| 399 | + await surroundWith(SurroundWithKind.comments, languageClient); |
| 400 | + })); |
| 401 | + |
| 402 | + |
| 403 | + // Surround with CDATA |
| 404 | + context.subscriptions.push(commands.registerCommand(ClientCommandConstants.REFACTOR_SURROUND_WITH_CDATA, async () => { |
| 405 | + await surroundWith(SurroundWithKind.cdata, languageClient); |
| 406 | + })); |
| 407 | +} |
| 408 | + |
| 409 | +async function surroundWith(surroundWithType: SurroundWithKind, languageClient: LanguageClient) { |
| 410 | + const activeEditor = window.activeTextEditor; |
| 411 | + if (!activeEditor) { |
| 412 | + return; |
| 413 | + } |
| 414 | + const selection = activeEditor.selections[0]; |
| 415 | + if (!selection) { |
| 416 | + return; |
| 417 | + } |
| 418 | + |
| 419 | + const uri = window.activeTextEditor.document.uri; |
| 420 | + const identifier = TextDocumentIdentifier.create(uri.toString()); |
| 421 | + const range = languageClient.code2ProtocolConverter.asRange(selection); |
| 422 | + const supportedSnippet: boolean = vscode.SnippetTextEdit ? true : false; |
| 423 | + |
| 424 | + let result: SurroundWithResponse; |
| 425 | + try { |
| 426 | + result = await commands.executeCommand(ServerCommandConstants.REFACTOR_SURROUND_WITH, identifier, range, surroundWithType, supportedSnippet); |
| 427 | + } catch (error) { |
| 428 | + console.log(`Error while surround with : ${error}`); |
| 429 | + } |
| 430 | + |
| 431 | + if (!result) { |
| 432 | + return; |
| 433 | + } |
| 434 | + |
| 435 | + const startTag = result.start.newText; |
| 436 | + const endTag = result.end.newText; |
| 437 | + |
| 438 | + if (supportedSnippet) { |
| 439 | + // SnippetTextEdit is supported, uses snippet (with choice) to manage cursor. |
| 440 | + const startRange = languageClient.protocol2CodeConverter.asRange(result.start.range); |
| 441 | + const endRange = languageClient.protocol2CodeConverter.asRange(result.end.range); |
| 442 | + const snippetEdits = [new vscode.SnippetTextEdit(startRange, new SnippetString(startTag)), new vscode.SnippetTextEdit(endRange, new SnippetString(endTag))]; |
| 443 | + const edit = new WorkspaceEdit(); |
| 444 | + edit.set(activeEditor.document.uri, snippetEdits); |
| 445 | + await workspace.applyEdit(edit); |
| 446 | + } else { |
| 447 | + // SnippetTextEdit is not supported, update start / end tag |
| 448 | + const startPos = languageClient.protocol2CodeConverter.asPosition(result.start.range.start); |
| 449 | + const endPos = languageClient.protocol2CodeConverter.asPosition(result.end.range.start); |
| 450 | + activeEditor.edit((selectedText) => { |
| 451 | + selectedText.insert(startPos, startTag); |
| 452 | + selectedText.insert(endPos, endTag); |
| 453 | + }) |
| 454 | + |
| 455 | + if (surroundWithType === SurroundWithKind.tags) { |
| 456 | + // Force the show of completion |
| 457 | + const pos = languageClient.protocol2CodeConverter.asPosition(result.start.range.start); |
| 458 | + const posAfterStartBracket = new Position(pos.line, pos.character + 1); |
| 459 | + activeEditor.selections = [new Selection(posAfterStartBracket, posAfterStartBracket)]; |
| 460 | + commands.executeCommand("editor.action.triggerSuggest"); |
| 461 | + } |
| 462 | + } |
| 463 | + |
| 464 | +} |
0 commit comments