/Extension/src/LanguageServer/Providers/renameProvider.ts

https://github.com/Microsoft/vscode-cpptools · TypeScript · 121 lines · 102 code · 4 blank · 15 comment · 25 complexity · e0a4665497b02ce7bcb673c0284e94cd MD5 · raw file

  1. /* --------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All Rights Reserved.
  3. * See 'LICENSE' in the project root for license information.
  4. * ------------------------------------------------------------------------------------------ */
  5. import * as vscode from 'vscode';
  6. import { DefaultClient, workspaceReferences, ReferencesCancellationState, RenameParams, CancelReferencesNotification } from '../client';
  7. import * as refs from '../references';
  8. import { CppSettings } from '../settings';
  9. import { Position } from 'vscode-languageclient';
  10. import * as nls from 'vscode-nls';
  11. import * as util from '../../common';
  12. nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
  13. const localize: nls.LocalizeFunc = nls.loadMessageBundle();
  14. export class RenameProvider implements vscode.RenameProvider {
  15. private client: DefaultClient;
  16. constructor(client: DefaultClient) {
  17. this.client = client;
  18. }
  19. public async provideRenameEdits(document: vscode.TextDocument, position: vscode.Position, newName: string, token: vscode.CancellationToken): Promise<vscode.WorkspaceEdit> {
  20. const settings: CppSettings = new CppSettings();
  21. if (settings.renameRequiresIdentifier && !util.isValidIdentifier(newName)) {
  22. vscode.window.showErrorMessage(localize("invalid.identifier.for.rename", "Invalid identifier provided for the Rename Symbol operation."));
  23. const workspaceEdit: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
  24. return Promise.resolve(workspaceEdit);
  25. }
  26. // Normally, VS Code considers rename to be an atomic operation.
  27. // If the user clicks anywhere in the document, it attempts to cancel it.
  28. // Because that prevents our rename UI, we ignore cancellation requests.
  29. // VS Code will attempt to issue new rename requests while another is still active.
  30. // When we receive another rename request, cancel the one that is in progress.
  31. DefaultClient.renamePending = true;
  32. ++DefaultClient.renameRequestsPending;
  33. return new Promise<vscode.WorkspaceEdit>((resolve, reject) => {
  34. const callback: () => void = () => {
  35. const params: RenameParams = {
  36. newName: newName,
  37. position: Position.create(position.line, position.character),
  38. textDocument: this.client.languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document)
  39. };
  40. DefaultClient.referencesParams = params;
  41. this.client.notifyWhenReady(() => {
  42. // The current request is represented by referencesParams. If a request detects
  43. // referencesParams does not match the object used when creating the request, abort it.
  44. if (params !== DefaultClient.referencesParams) {
  45. if (--DefaultClient.renameRequestsPending === 0) {
  46. DefaultClient.renamePending = false;
  47. }
  48. // Complete with nothing instead of rejecting, to avoid an error message from VS Code
  49. const workspaceEdit: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
  50. resolve(workspaceEdit);
  51. return;
  52. }
  53. DefaultClient.referencesRequestPending = true;
  54. workspaceReferences.setResultsCallback((referencesResult: refs.ReferencesResult | null, doResolve: boolean) => {
  55. DefaultClient.referencesRequestPending = false;
  56. --DefaultClient.renameRequestsPending;
  57. const workspaceEdit: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
  58. const cancelling: boolean = DefaultClient.referencesPendingCancellations.length > 0;
  59. if (cancelling) {
  60. while (DefaultClient.referencesPendingCancellations.length > 1) {
  61. const pendingCancel: ReferencesCancellationState = DefaultClient.referencesPendingCancellations[0];
  62. DefaultClient.referencesPendingCancellations.pop();
  63. pendingCancel.reject();
  64. }
  65. const pendingCancel: ReferencesCancellationState = DefaultClient.referencesPendingCancellations[0];
  66. DefaultClient.referencesPendingCancellations.pop();
  67. pendingCancel.callback();
  68. } else {
  69. if (DefaultClient.renameRequestsPending === 0) {
  70. DefaultClient.renamePending = false;
  71. }
  72. // If rename UI was canceled, we will get a null result.
  73. // If null, return an empty list to avoid Rename failure dialog.
  74. if (referencesResult) {
  75. for (const reference of referencesResult.referenceInfos) {
  76. const uri: vscode.Uri = vscode.Uri.file(reference.file);
  77. const range: vscode.Range = new vscode.Range(reference.position.line, reference.position.character, reference.position.line, reference.position.character + referencesResult.text.length);
  78. const metadata: vscode.WorkspaceEditEntryMetadata = {
  79. needsConfirmation: reference.type !== refs.ReferenceType.Confirmed,
  80. label: refs.getReferenceTagString(reference.type, false, true),
  81. iconPath: refs.getReferenceItemIconPath(reference.type, false)
  82. };
  83. workspaceEdit.replace(uri, range, newName, metadata);
  84. }
  85. }
  86. }
  87. if (referencesResult && (referencesResult.referenceInfos === null || referencesResult.referenceInfos.length === 0)) {
  88. vscode.window.showErrorMessage(localize("unable.to.locate.selected.symbol", "A definition for the selected symbol could not be located."));
  89. }
  90. resolve(workspaceEdit);
  91. });
  92. workspaceReferences.startRename(params);
  93. });
  94. };
  95. if (DefaultClient.referencesRequestPending || workspaceReferences.symbolSearchInProgress) {
  96. const cancelling: boolean = DefaultClient.referencesPendingCancellations.length > 0;
  97. DefaultClient.referencesPendingCancellations.push({
  98. reject: () => {
  99. --DefaultClient.renameRequestsPending;
  100. // Complete with nothing instead of rejecting, to avoid an error message from VS Code
  101. const workspaceEdit: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
  102. resolve(workspaceEdit);
  103. }, callback
  104. });
  105. if (!cancelling) {
  106. workspaceReferences.referencesCanceled = true;
  107. if (!DefaultClient.referencesRequestPending) {
  108. workspaceReferences.referencesCanceledWhilePreviewing = true;
  109. }
  110. this.client.languageClient.sendNotification(CancelReferencesNotification);
  111. }
  112. } else {
  113. callback();
  114. }
  115. });
  116. }
  117. }