Repository: denoland/deno
Pull request: fix(lsp): include prefix and suffix for rename edits

Fixes #28074.

Changed files:
filename: cli/lsp/language_server.rs
patch: @@ -3017,12 +3017,15 @@ impl Inner {
     let asset_or_doc = self.get_asset_or_document(&specifier)?;
     let line_index = asset_or_doc.line_index();
 
+    let user_preferences =
+      tsc::UserPreferences::from_config_for_specifier(&self.config, &specifier);
     let maybe_locations = self
       .ts_server
       .find_rename_locations(
         self.snapshot(),
         specifier,
         line_index.offset_tsc(params.text_document_position.position)?,
+        user_preferences,
         token,
       )
       .await

filename: cli/lsp/tsc.rs
patch: @@ -1174,14 +1174,15 @@ impl TsServer {
     snapshot: Arc<StateSnapshot>,
     specifier: ModuleSpecifier,
     position: u32,
+    user_preferences: UserPreferences,
     token: &CancellationToken,
   ) -> Result<Option<Vec<RenameLocation>>, AnyError> {
     let req = TscRequest::FindRenameLocations((
       self.specifier_map.denormalize(&specifier),
       position,
       false,
       false,
-      false,
+      user_preferences,
     ));
     let mut results = FuturesOrdered::new();
     for scope in snapshot
@@ -1205,7 +1206,10 @@ impl TsServer {
         if token.is_cancelled() {
           return Err(anyhow!("request cancelled"));
         } else {
-          lsp_warn!("Unable to get rename locations from TypeScript: {err}");
+          let err = err.to_string();
+          if !err.contains("Could not find source file") {
+            lsp_warn!("Unable to get rename locations from TypeScript: {err}");
+          }
         }
       }
       let locations = locations.unwrap_or_default();
@@ -2420,9 +2424,8 @@ impl ImplementationLocation {
 pub struct RenameLocation {
   #[serde(flatten)]
   document_span: DocumentSpan,
-  // RenameLocation props
-  // prefix_text: Option<String>,
-  // suffix_text: Option<String>,
+  prefix_text: Option<String>,
+  suffix_text: Option<String>,
 }
 
 impl RenameLocation {
@@ -2481,12 +2484,21 @@ impl RenameLocations {
 
       // push TextEdit for ensured `TextDocumentEdit.edits`.
       let document_edit = text_document_edit_map.get_mut(&uri).unwrap();
+      let new_text = [
+        location.prefix_text.as_deref(),
+        Some(new_name),
+        location.suffix_text.as_deref(),
+      ]
+      .into_iter()
+      .flatten()
+      .collect::<Vec<_>>()
+      .join("");
       document_edit.edits.push(lsp::OneOf::Left(lsp::TextEdit {
         range: location
           .document_span
           .text_span
           .to_range(asset_or_doc.line_index()),
-        new_text: new_name.to_string(),
+        new_text,
       }));
     }
 
@@ -5548,8 +5560,8 @@ pub enum TscRequest {
   ProvideCallHierarchyOutgoingCalls((String, u32)),
   // https://github.com/denoland/deno/blob/v1.37.1/cli/tsc/dts/typescript.d.ts#L6236
   PrepareCallHierarchy((String, u32)),
-  // https://github.com/denoland/deno/blob/v1.37.1/cli/tsc/dts/typescript.d.ts#L6221
-  FindRenameLocations((String, u32, bool, bool, bool)),
+  // https://github.com/denoland/deno/blob/v2.2.2/cli/tsc/dts/typescript.d.ts#L6674
+  FindRenameLocations((String, u32, bool, bool, UserPreferences)),
   // https://github.com/denoland/deno/blob/v1.37.1/cli/tsc/dts/typescript.d.ts#L6224
   GetSmartSelectionRange((String, u32)),
   // https://github.com/denoland/deno/blob/v1.37.1/cli/tsc/dts/typescript.d.ts#L6183

filename: tests/integration/lsp_tests.rs
patch: @@ -3105,7 +3105,7 @@ fn lsp_rename_synbol_file_scheme_edits_only() {
                 "start": { "line": 1, "character": 17 },
                 "end": { "line": 1, "character": 26 },
               },
-              "newText": "PATH_SEPARATOR",
+              "newText": "SEPARATOR as PATH_SEPARATOR",
             },
             {
               "range": {
@@ -3879,50 +3879,65 @@ fn lsp_rename() {
   let context = TestContextBuilder::new().use_temp_cwd().build();
   let mut client = context.new_lsp_command().build();
   client.initialize_default();
-  client.did_open(
-    json!({
-      "textDocument": {
-        "uri": "file:///a/file.ts",
-        "languageId": "typescript",
-        "version": 1,
+  client.did_open(json!({
+    "textDocument": {
+      "uri": "file:///a/file.ts",
+      "languageId": "typescript",
+      "version": 1,
+      "text": r#"
+        const variable = "a";
+        console.log(variable);
+        ({ variable });
+
         // this should not rename in comments and strings
-        "text": "let variable = 'a'; // variable\nconsole.log(variable);\n\"variable\";\n"
-      }
-    }),
-  );
+        // variable
+        "variable";
+      "#,
+    },
+  }));
   let res = client.write_request(
     "textDocument/rename",
     json!({
-      "textDocument": {
-        "uri": "file:///a/file.ts"
-      },
-      "position": { "line": 0, "character": 4 },
-      "newName": "variable_modified"
+      "textDocument": { "uri": "file:///a/file.ts" },
+      "position": { "line": 1, "character": 14 },
+      "newName": "variable_modified",
     }),
   );
   assert_eq!(
     res,
     json!({
-      "documentChanges": [{
-        "textDocument": {
-          "uri": "file:///a/file.ts",
-          "version": 1
-        },
-        "edits": [{
-          "range": {
-            "start": { "line": 0, "character": 4 },
-            "end": { "line": 0, "character": 12 }
-          },
-          "newText": "variable_modified"
-        }, {
-          "range": {
-            "start": { "line": 1, "character": 12 },
-            "end": { "line": 1, "character": 20 }
+      "documentChanges": [
+        {
+          "textDocument": {
+            "uri": "file:///a/file.ts",
+            "version": 1,
           },
-          "newText": "variable_modified"
-        }]
-      }]
-    })
+          "edits": [
+            {
+              "range": {
+                "start": { "line": 1, "character": 14 },
+                "end": { "line": 1, "character": 22 },
+              },
+              "newText": "variable_modified",
+            },
+            {
+              "range": {
+                "start": { "line": 2, "character": 20 },
+                "end": { "line": 2, "character": 28 },
+              },
+              "newText": "variable_modified",
+            },
+            {
+              "range": {
+                "start": { "line": 3, "character": 11 },
+                "end": { "line": 3, "character": 19 },
+              },
+              "newText": "variable: variable_modified",
+            },
+          ],
+        },
+      ],
+    }),
   );
   client.shutdown();
 }
@@ -15353,7 +15368,7 @@ fn lsp_deno_json_scopes_rename_symbol() {
           "edits": [
             {
               "range": file2.range_of("foo"),
-              "newText": "bar",
+              "newText": "bar as foo",
             },
           ],
         },