fix(lsp): pass quote preference to tsc (#20547)

This commit is contained in:
Nayeem Rahman 2023-09-18 20:48:32 +01:00 committed by GitHub
parent f7ba701304
commit 86c04f43e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 260 additions and 20 deletions

View File

@ -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| {

View File

@ -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"
);
}

View File

@ -4906,6 +4906,174 @@ fn lsp_code_actions_refactor() {
client.shutdown();
}
#[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();

View File

@ -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,
),
);
}

View File

@ -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 {