diff options
-rw-r--r-- | cli/lsp/language_server.rs | 29 | ||||
-rw-r--r-- | cli/lsp/tsc.rs | 61 | ||||
-rw-r--r-- | cli/tests/integration/lsp_tests.rs | 168 | ||||
-rw-r--r-- | cli/tsc/99_main_compiler.js | 14 | ||||
-rw-r--r-- | cli/tsc/compiler.d.ts | 8 |
5 files changed, 260 insertions, 20 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index df5552764..102408df4 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1927,6 +1927,10 @@ impl Inner { ..line_index.offset_tsc(diagnostic.range.end)?, codes, (&self.fmt_options.options).into(), + tsc::UserPreferences { + quote_preference: Some((&self.fmt_options.options).into()), + ..self.config.workspace_settings().into() + }, ) .await; for action in actions { @@ -1984,6 +1988,10 @@ impl Inner { specifier.clone(), line_index.offset_tsc(params.range.start)? ..line_index.offset_tsc(params.range.end)?, + Some(tsc::UserPreferences { + quote_preference: Some((&self.fmt_options.options).into()), + ..self.config.workspace_settings().into() + }), only, ) .await?; @@ -2039,6 +2047,10 @@ impl Inner { self.snapshot(), &code_action_data, (&self.fmt_options.options).into(), + tsc::UserPreferences { + quote_preference: Some((&self.fmt_options.options).into()), + ..self.config.workspace_settings().into() + }, ) .await?; if combined_code_actions.commands.is_some() { @@ -2084,6 +2096,10 @@ impl Inner { ..line_index.offset_tsc(action_data.range.end)?, action_data.refactor_name, action_data.action_name, + Some(tsc::UserPreferences { + quote_preference: Some((&self.fmt_options.options).into()), + ..self.config.workspace_settings().into() + }), ) .await?; code_action.edit = refactor_edit_info.to_workspace_edit(self).await?; @@ -2399,6 +2415,7 @@ impl Inner { position, tsc::GetCompletionsAtPositionOptions { user_preferences: tsc::UserPreferences { + quote_preference: Some((&self.fmt_options.options).into()), allow_incomplete_completions: Some(true), allow_text_changes_in_new_files: Some( specifier.scheme() == "file", @@ -2466,10 +2483,14 @@ impl Inner { })?; if let Some(data) = &data.tsc { let specifier = &data.specifier; - let args = GetCompletionDetailsArgs { + let mut args = GetCompletionDetailsArgs { format_code_settings: Some((&self.fmt_options.options).into()), ..data.into() }; + args + .preferences + .get_or_insert(Default::default()) + .quote_preference = Some((&self.fmt_options.options).into()); let result = self .ts_server .get_completion_details(self.snapshot(), args) @@ -2971,6 +2992,7 @@ impl Inner { (&self.fmt_options.options).into(), tsc::UserPreferences { allow_text_changes_in_new_files: Some(true), + quote_preference: Some((&self.fmt_options.options).into()), ..Default::default() }, ) @@ -3600,7 +3622,10 @@ impl Inner { self.snapshot(), specifier, text_span, - workspace_settings.into(), + tsc::UserPreferences { + quote_preference: Some((&self.fmt_options.options).into()), + ..workspace_settings.into() + }, ) .await?; let maybe_inlay_hints = maybe_inlay_hints.map(|hints| { diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 2c63131b6..81950f2e3 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -234,6 +234,7 @@ impl TsServer { range: Range<u32>, codes: Vec<String>, format_code_settings: FormatCodeSettings, + preferences: UserPreferences, ) -> Vec<CodeFixAction> { let req = RequestMethod::GetCodeFixes(( specifier, @@ -241,6 +242,7 @@ impl TsServer { range.end, codes, format_code_settings, + preferences, )); match self.request(snapshot, req).await { Ok(items) => items, @@ -260,6 +262,7 @@ impl TsServer { snapshot: Arc<StateSnapshot>, specifier: ModuleSpecifier, range: Range<u32>, + preferences: Option<UserPreferences>, only: String, ) -> Result<Vec<ApplicableRefactorInfo>, LspError> { let req = RequestMethod::GetApplicableRefactors(( @@ -268,6 +271,7 @@ impl TsServer { start: range.start, length: range.end - range.start, }, + preferences, only, )); self.request(snapshot, req).await.map_err(|err| { @@ -281,11 +285,13 @@ impl TsServer { snapshot: Arc<StateSnapshot>, code_action_data: &CodeActionData, format_code_settings: FormatCodeSettings, + preferences: UserPreferences, ) -> Result<CombinedCodeActions, LspError> { let req = RequestMethod::GetCombinedCodeFix(( code_action_data.specifier.clone(), json!(code_action_data.fix_id.clone()), format_code_settings, + preferences, )); self.request(snapshot, req).await.map_err(|err| { log::error!("Unable to get combined fix from TypeScript: {}", err); @@ -293,6 +299,7 @@ impl TsServer { }) } + #[allow(clippy::too_many_arguments)] pub async fn get_edits_for_refactor( &self, snapshot: Arc<StateSnapshot>, @@ -301,6 +308,7 @@ impl TsServer { range: Range<u32>, refactor_name: String, action_name: String, + preferences: Option<UserPreferences>, ) -> Result<RefactorEditInfo, LspError> { let req = RequestMethod::GetEditsForRefactor(( specifier, @@ -311,6 +319,7 @@ impl TsServer { }, refactor_name, action_name, + preferences, )); self.request(snapshot, req).await.map_err(|err| { log::error!("Failed to request to tsserver {}", err); @@ -3507,6 +3516,15 @@ pub enum QuotePreference { Single, } +impl From<&FmtOptionsConfig> for QuotePreference { + fn from(config: &FmtOptionsConfig) -> Self { + match config.single_quote { + Some(true) => QuotePreference::Single, + _ => QuotePreference::Double, + } + } +} + #[derive(Debug, Serialize)] #[serde(rename_all = "kebab-case")] #[allow(dead_code)] @@ -3765,7 +3783,9 @@ enum RequestMethod { }, GetAssets, /// Retrieve the possible refactor info for a range of a file. - GetApplicableRefactors((ModuleSpecifier, TextSpan, String)), + GetApplicableRefactors( + (ModuleSpecifier, TextSpan, Option<UserPreferences>, String), + ), /// Retrieve the refactor edit info for a range. GetEditsForRefactor( ( @@ -3774,6 +3794,7 @@ enum RequestMethod { TextSpan, String, String, + Option<UserPreferences>, ), ), /// Retrieve the refactor edit info for a range. @@ -3786,7 +3807,16 @@ enum RequestMethod { ), ), /// Retrieve code fixes for a range of a file with the provided error codes. - GetCodeFixes((ModuleSpecifier, u32, u32, Vec<String>, FormatCodeSettings)), + GetCodeFixes( + ( + ModuleSpecifier, + u32, + u32, + Vec<String>, + FormatCodeSettings, + UserPreferences, + ), + ), /// Get completion information at a given position (IntelliSense). GetCompletions( ( @@ -3799,7 +3829,9 @@ enum RequestMethod { /// Get details about a specific completion entry. GetCompletionDetails(GetCompletionDetailsArgs), /// Retrieve the combined code fixes for a fix id for a module. - GetCombinedCodeFix((ModuleSpecifier, Value, FormatCodeSettings)), + GetCombinedCodeFix( + (ModuleSpecifier, Value, FormatCodeSettings, UserPreferences), + ), /// Get declaration information for a specific position. GetDefinition((ModuleSpecifier, u32)), /// Return diagnostics for given file. @@ -3876,11 +3908,17 @@ impl RequestMethod { "id": id, "method": "getAssets", }), - RequestMethod::GetApplicableRefactors((specifier, span, kind)) => json!({ + RequestMethod::GetApplicableRefactors(( + specifier, + span, + preferences, + kind, + )) => json!({ "id": id, "method": "getApplicableRefactors", "specifier": state.denormalize_specifier(specifier), "range": { "pos": span.start, "end": span.start + span.length }, + "preferences": preferences, "kind": kind, }), RequestMethod::GetEditsForRefactor(( @@ -3889,6 +3927,7 @@ impl RequestMethod { span, refactor_name, action_name, + preferences, )) => json!({ "id": id, "method": "getEditsForRefactor", @@ -3897,6 +3936,7 @@ impl RequestMethod { "range": { "pos": span.start, "end": span.start + span.length}, "refactorName": refactor_name, "actionName": action_name, + "preferences": preferences, }), RequestMethod::GetEditsForFileRename(( old_specifier, @@ -3917,6 +3957,7 @@ impl RequestMethod { end_pos, error_codes, format_code_settings, + preferences, )) => json!({ "id": id, "method": "getCodeFixes", @@ -3925,17 +3966,20 @@ impl RequestMethod { "endPosition": end_pos, "errorCodes": error_codes, "formatCodeSettings": format_code_settings, + "preferences": preferences, }), RequestMethod::GetCombinedCodeFix(( specifier, fix_id, format_code_settings, + preferences, )) => json!({ "id": id, "method": "getCombinedCodeFix", "specifier": state.denormalize_specifier(specifier), "fixId": fix_id, "formatCodeSettings": format_code_settings, + "preferences": preferences, }), RequestMethod::GetCompletionDetails(args) => json!({ "id": id, @@ -4976,6 +5020,7 @@ mod tests { assert!(result.is_ok()); let fmt_options_config = FmtOptionsConfig { semi_colons: Some(false), + single_quote: Some(true), ..Default::default() }; let result = request( @@ -4986,6 +5031,7 @@ mod tests { position, GetCompletionsAtPositionOptions { user_preferences: UserPreferences { + quote_preference: Some((&fmt_options_config).into()), include_completions_for_module_exports: Some(true), include_completions_with_insert_text: Some(true), ..Default::default() @@ -5011,7 +5057,10 @@ mod tests { position, name: entry.name.clone(), source: entry.source.clone(), - preferences: None, + preferences: Some(UserPreferences { + quote_preference: Some((&fmt_options_config).into()), + ..Default::default() + }), format_code_settings: Some((&fmt_options_config).into()), data: entry.data.clone(), }), @@ -5029,7 +5078,7 @@ mod tests { let change = changes.text_changes.first().unwrap(); assert_eq!( change.new_text, - "import { someLongVariable } from \"./b.ts\"\n" + "import { someLongVariable } from './b.ts'\n" ); } diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs index de9626620..8b265f911 100644 --- a/cli/tests/integration/lsp_tests.rs +++ b/cli/tests/integration/lsp_tests.rs @@ -4907,6 +4907,174 @@ fn lsp_code_actions_refactor() { } #[test] +fn lsp_code_actions_imports_respects_fmt_config() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let temp_dir = context.temp_dir(); + temp_dir.write( + "./deno.jsonc", + json!({ + "fmt": { + "semiColons": false, + "singleQuote": true, + } + }) + .to_string(), + ); + temp_dir.write( + "file00.ts", + r#" + export interface MallardDuckConfigOptions extends DuckConfigOptions { + kind: "mallard"; + } + "#, + ); + temp_dir.write( + "file01.ts", + r#" + export interface DuckConfigOptions { + kind: string; + quacks: boolean; + } + "#, + ); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.did_open(json!({ + "textDocument": { + "uri": temp_dir.uri().join("file00.ts").unwrap(), + "languageId": "typescript", + "version": 1, + "text": temp_dir.read_to_string("file00.ts"), + } + })); + client.did_open(json!({ + "textDocument": { + "uri": temp_dir.uri().join("file01.ts").unwrap(), + "languageId": "typescript", + "version": 1, + "text": temp_dir.read_to_string("file01.ts"), + } + })); + + let res = client.write_request( + "textDocument/codeAction", + json!({ + "textDocument": { + "uri": temp_dir.uri().join("file00.ts").unwrap() + }, + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 4, "character": 0 } + }, + "context": { + "diagnostics": [{ + "range": { + "start": { "line": 1, "character": 55 }, + "end": { "line": 1, "character": 64 } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'DuckConfigOptions'." + }], + "only": ["quickfix"] + } + }), + ); + assert_eq!( + res, + json!([{ + "title": "Add import from \"./file01.ts\"", + "kind": "quickfix", + "diagnostics": [{ + "range": { + "start": { "line": 1, "character": 55 }, + "end": { "line": 1, "character": 64 } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'DuckConfigOptions'." + }], + "edit": { + "documentChanges": [{ + "textDocument": { + "uri": temp_dir.uri().join("file00.ts").unwrap(), + "version": 1 + }, + "edits": [{ + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 0 } + }, + "newText": "import { DuckConfigOptions } from './file01.ts'\n" + }] + }] + } + }]) + ); + let res = client.write_request( + "codeAction/resolve", + json!({ + "title": "Add all missing imports", + "kind": "quickfix", + "diagnostics": [{ + "range": { + "start": { "line": 1, "character": 55 }, + "end": { "line": 1, "character": 64 } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'DuckConfigOptions'." + }], + "data": { + "specifier": temp_dir.uri().join("file00.ts").unwrap(), + "fixId": "fixMissingImport" + } + }), + ); + assert_eq!( + res, + json!({ + "title": "Add all missing imports", + "kind": "quickfix", + "diagnostics": [{ + "range": { + "start": { "line": 1, "character": 55 }, + "end": { "line": 1, "character": 64 } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'DuckConfigOptions'." + }], + "edit": { + "documentChanges": [{ + "textDocument": { + "uri": temp_dir.uri().join("file00.ts").unwrap(), + "version": 1 + }, + "edits": [{ + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 0 } + }, + "newText": "import { DuckConfigOptions } from './file01.ts'\n" + }] + }] + }, + "data": { + "specifier": temp_dir.uri().join("file00.ts").unwrap(), + "fixId": "fixMissingImport" + } + }) + ); + + client.shutdown(); +} + +#[test] fn lsp_code_actions_refactor_no_disabled_support() { let context = TestContextBuilder::new().use_temp_cwd().build(); let mut client = context.new_lsp_command().build(); diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index 451e36b96..8978bf1be 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -1020,7 +1020,7 @@ delete Object.prototype.__proto__; request.specifier, request.range, { - quotePreference: "double", + ...(request.preferences ?? {}), allowTextChangesInNewFiles: true, provideRefactorNotApplicableReason: true, }, @@ -1043,9 +1043,7 @@ delete Object.prototype.__proto__; request.range, request.refactorName, request.actionName, - { - quotePreference: "double", - }, + request.preferences, ), ); } @@ -1072,9 +1070,7 @@ delete Object.prototype.__proto__; ...request.formatCodeSettings, indentStyle: ts.IndentStyle.Block, }, - { - quotePreference: "double", - }, + request.preferences, ), ); } @@ -1091,9 +1087,7 @@ delete Object.prototype.__proto__; ...request.formatCodeSettings, indentStyle: ts.IndentStyle.Block, }, - { - quotePreference: "double", - }, + request.preferences, ), ); } diff --git a/cli/tsc/compiler.d.ts b/cli/tsc/compiler.d.ts index 7b8340093..30e433405 100644 --- a/cli/tsc/compiler.d.ts +++ b/cli/tsc/compiler.d.ts @@ -116,6 +116,7 @@ declare global { method: "getApplicableRefactors"; specifier: string; range: ts.TextRange; + preferences?: ts.UserPreferences; kind: string; } @@ -126,12 +127,13 @@ declare global { range: ts.TextRange; refactorName: string; actionName: string; + preferences?: ts.UserPreferences; } interface GetEditsForFileRename extends BaseLanguageServerRequest { method: "getEditsForFileRename"; - old_specifier: string; - new_specifier: string; + oldSpecifier: string; + newSpecifier: string; formatCodeSettings: ts.FormatCodeSettings; preferences?: ts.UserPreferences; } @@ -143,6 +145,7 @@ declare global { endPosition: number; errorCodes: string[]; formatCodeSettings: ts.FormatCodeSettings; + preferences: ts.UserPreferences; } interface GetCombinedCodeFix extends BaseLanguageServerRequest { @@ -151,6 +154,7 @@ declare global { // deno-lint-ignore ban-types fixId: {}; formatCodeSettings: ts.FormatCodeSettings; + preferences: ts.UserPreferences; } interface GetCompletionDetails extends BaseLanguageServerRequest { |