summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/lsp/config.rs29
-rw-r--r--cli/lsp/language_server.rs1
-rw-r--r--cli/lsp/tsc.rs111
-rw-r--r--cli/tests/integration/lsp_tests.rs145
4 files changed, 250 insertions, 36 deletions
diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs
index 9af05484c..6a005e83b 100644
--- a/cli/lsp/config.rs
+++ b/cli/lsp/config.rs
@@ -6,6 +6,7 @@ use crate::lsp::logging::lsp_warn;
use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::path::specifier_to_file_path;
use deno_ast::MediaType;
+use deno_config::FmtOptionsConfig;
use deno_core::parking_lot::Mutex;
use deno_core::serde::de::DeserializeOwned;
use deno_core::serde::Deserialize;
@@ -356,6 +357,29 @@ impl Default for JsxAttributeCompletionStyle {
}
}
+#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
+#[serde(rename_all = "kebab-case")]
+pub enum QuoteStyle {
+ Auto,
+ Double,
+ Single,
+}
+
+impl Default for QuoteStyle {
+ fn default() -> Self {
+ Self::Auto
+ }
+}
+
+impl From<&FmtOptionsConfig> for QuoteStyle {
+ fn from(config: &FmtOptionsConfig) -> Self {
+ match config.single_quote {
+ Some(true) => QuoteStyle::Single,
+ _ => QuoteStyle::Double,
+ }
+ }
+}
+
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct LanguagePreferences {
@@ -367,6 +391,8 @@ pub struct LanguagePreferences {
pub auto_import_file_exclude_patterns: Vec<String>,
#[serde(default = "is_true")]
pub use_aliases_for_renames: bool,
+ #[serde(default)]
+ pub quote_style: QuoteStyle,
}
impl Default for LanguagePreferences {
@@ -376,6 +402,7 @@ impl Default for LanguagePreferences {
jsx_attribute_completion_style: Default::default(),
auto_import_file_exclude_patterns: vec![],
use_aliases_for_renames: true,
+ quote_style: Default::default(),
}
}
}
@@ -1372,6 +1399,7 @@ mod tests {
jsx_attribute_completion_style: JsxAttributeCompletionStyle::Auto,
auto_import_file_exclude_patterns: vec![],
use_aliases_for_renames: true,
+ quote_style: QuoteStyle::Auto,
},
suggest: CompletionSettings {
complete_function_calls: false,
@@ -1416,6 +1444,7 @@ mod tests {
jsx_attribute_completion_style: JsxAttributeCompletionStyle::Auto,
auto_import_file_exclude_patterns: vec![],
use_aliases_for_renames: true,
+ quote_style: QuoteStyle::Auto,
},
suggest: CompletionSettings {
complete_function_calls: false,
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 2a36708fb..e6e49c654 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -3014,7 +3014,6 @@ 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()
},
)
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index da6b229c8..7f650348b 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -99,24 +99,90 @@ type Request = (
CancellationToken,
);
-/// Relevant subset of https://github.com/denoland/deno/blob/80331d1fe5b85b829ac009fdc201c128b3427e11/cli/tsc/dts/typescript.d.ts#L6658.
+#[derive(Debug, Clone, Copy, Serialize_repr)]
+#[repr(u8)]
+pub enum IndentStyle {
+ #[allow(dead_code)]
+ None = 0,
+ Block = 1,
+ #[allow(dead_code)]
+ Smart = 2,
+}
+
+/// Relevant subset of https://github.com/denoland/deno/blob/v1.37.1/cli/tsc/dts/typescript.d.ts#L6658.
#[derive(Clone, Debug, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FormatCodeSettings {
- convert_tabs_to_spaces: Option<bool>,
+ base_indent_size: Option<u8>,
indent_size: Option<u8>,
+ tab_size: Option<u8>,
+ new_line_character: Option<String>,
+ convert_tabs_to_spaces: Option<bool>,
+ indent_style: Option<IndentStyle>,
+ trim_trailing_whitespace: Option<bool>,
+ insert_space_after_comma_delimiter: Option<bool>,
+ insert_space_after_semicolon_in_for_statements: Option<bool>,
+ insert_space_before_and_after_binary_operators: Option<bool>,
+ insert_space_after_constructor: Option<bool>,
+ insert_space_after_keywords_in_control_flow_statements: Option<bool>,
+ insert_space_after_function_keyword_for_anonymous_functions: Option<bool>,
+ insert_space_after_opening_and_before_closing_nonempty_parenthesis:
+ Option<bool>,
+ insert_space_after_opening_and_before_closing_nonempty_brackets: Option<bool>,
+ insert_space_after_opening_and_before_closing_nonempty_braces: Option<bool>,
+ insert_space_after_opening_and_before_closing_template_string_braces:
+ Option<bool>,
+ insert_space_after_opening_and_before_closing_jsx_expression_braces:
+ Option<bool>,
+ insert_space_after_type_assertion: Option<bool>,
+ insert_space_before_function_parenthesis: Option<bool>,
+ place_open_brace_on_new_line_for_functions: Option<bool>,
+ place_open_brace_on_new_line_for_control_blocks: Option<bool>,
+ insert_space_before_type_annotation: Option<bool>,
+ indent_multi_line_object_literal_beginning_on_blank_line: Option<bool>,
semicolons: Option<SemicolonPreference>,
+ indent_switch_case: Option<bool>,
}
impl From<&FmtOptionsConfig> for FormatCodeSettings {
fn from(config: &FmtOptionsConfig) -> Self {
FormatCodeSettings {
- convert_tabs_to_spaces: Some(!config.use_tabs.unwrap_or(false)),
+ base_indent_size: Some(0),
indent_size: Some(config.indent_width.unwrap_or(2)),
+ tab_size: Some(config.indent_width.unwrap_or(2)),
+ new_line_character: Some("\n".to_string()),
+ convert_tabs_to_spaces: Some(!config.use_tabs.unwrap_or(false)),
+ indent_style: Some(IndentStyle::Block),
+ trim_trailing_whitespace: Some(false),
+ insert_space_after_comma_delimiter: Some(true),
+ insert_space_after_semicolon_in_for_statements: Some(true),
+ insert_space_before_and_after_binary_operators: Some(true),
+ insert_space_after_constructor: Some(false),
+ insert_space_after_keywords_in_control_flow_statements: Some(true),
+ insert_space_after_function_keyword_for_anonymous_functions: Some(true),
+ insert_space_after_opening_and_before_closing_nonempty_parenthesis: Some(
+ false,
+ ),
+ insert_space_after_opening_and_before_closing_nonempty_brackets: Some(
+ false,
+ ),
+ insert_space_after_opening_and_before_closing_nonempty_braces: Some(true),
+ insert_space_after_opening_and_before_closing_template_string_braces:
+ Some(false),
+ insert_space_after_opening_and_before_closing_jsx_expression_braces: Some(
+ false,
+ ),
+ insert_space_after_type_assertion: Some(false),
+ insert_space_before_function_parenthesis: Some(false),
+ place_open_brace_on_new_line_for_functions: Some(false),
+ place_open_brace_on_new_line_for_control_blocks: Some(false),
+ insert_space_before_type_annotation: Some(false),
+ indent_multi_line_object_literal_beginning_on_blank_line: Some(false),
semicolons: match config.semi_colons {
Some(false) => Some(SemicolonPreference::Remove),
_ => Some(SemicolonPreference::Insert),
},
+ indent_switch_case: Some(true),
}
}
}
@@ -294,9 +360,6 @@ impl TsServer {
format_code_settings: FormatCodeSettings,
preferences: UserPreferences,
) -> Vec<CodeFixAction> {
- let mut format_code_settings = json!(format_code_settings);
- let format_object = format_code_settings.as_object_mut().unwrap();
- format_object.insert("indentStyle".to_string(), json!(1));
let req = TscRequest {
method: "getCodeFixesAtPosition",
// https://github.com/denoland/deno/blob/v1.37.1/cli/tsc/dts/typescript.d.ts#L6257
@@ -363,9 +426,6 @@ impl TsServer {
format_code_settings: FormatCodeSettings,
preferences: UserPreferences,
) -> Result<CombinedCodeActions, LspError> {
- let mut format_code_settings = json!(format_code_settings);
- let format_object = format_code_settings.as_object_mut().unwrap();
- format_object.insert("indentStyle".to_string(), json!(1));
let req = TscRequest {
method: "getCombinedCodeFix",
// https://github.com/denoland/deno/blob/v1.37.1/cli/tsc/dts/typescript.d.ts#L6258
@@ -403,15 +463,6 @@ impl TsServer {
action_name: String,
preferences: Option<UserPreferences>,
) -> Result<RefactorEditInfo, LspError> {
- let mut format_code_settings = json!(format_code_settings);
- let format_object = format_code_settings.as_object_mut().unwrap();
- format_object.insert("indentStyle".to_string(), json!(2));
- format_object.insert(
- "insertSpaceBeforeAndAfterBinaryOperators".to_string(),
- json!(true),
- );
- format_object
- .insert("insertSpaceAfterCommaDelimiter".to_string(), json!(true));
let req = TscRequest {
method: "getEditsForRefactor",
// https://github.com/denoland/deno/blob/v1.37.1/cli/tsc/dts/typescript.d.ts#L6275
@@ -4024,23 +4075,7 @@ impl From<lsp::CompletionTriggerKind> for CompletionTriggerKind {
}
}
-#[derive(Debug, Serialize)]
-#[serde(rename_all = "kebab-case")]
-#[allow(dead_code)]
-pub enum QuotePreference {
- Auto,
- Double,
- Single,
-}
-
-impl From<&FmtOptionsConfig> for QuotePreference {
- fn from(config: &FmtOptionsConfig) -> Self {
- match config.single_quote {
- Some(true) => QuotePreference::Single,
- _ => QuotePreference::Double,
- }
- }
-}
+pub type QuotePreference = config::QuoteStyle;
pub type ImportModuleSpecifierPreference = config::ImportModuleSpecifier;
@@ -4270,6 +4305,12 @@ impl UserPreferences {
provide_prefix_and_suffix_text_for_rename: Some(
language_settings.preferences.use_aliases_for_renames,
),
+ // Only use workspace settings for quote style if there's no `deno.json`.
+ quote_preference: if config.has_config_file() {
+ base_preferences.quote_preference
+ } else {
+ Some(language_settings.preferences.quote_style)
+ },
..base_preferences
}
}
diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs
index 50b01b2ea..39b9b16b8 100644
--- a/cli/tests/integration/lsp_tests.rs
+++ b/cli/tests/integration/lsp_tests.rs
@@ -5570,6 +5570,151 @@ fn lsp_code_actions_imports_respects_fmt_config() {
}
#[test]
+fn lsp_quote_style_from_workspace_settings() {
+ let context = TestContextBuilder::new().use_temp_cwd().build();
+ let temp_dir = context.temp_dir();
+ 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.write_notification(
+ "workspace/didChangeConfiguration",
+ json!({
+ "settings": {}
+ }),
+ );
+ let settings = json!({
+ "typescript": {
+ "preferences": {
+ "quoteStyle": "single",
+ },
+ },
+ });
+ // one for the workspace
+ client.handle_configuration_request(&settings);
+ // one for the specifier
+ client.handle_configuration_request(&settings);
+
+ let code_action_params = 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": 56 },
+ "end": { "line": 1, "character": 73 },
+ },
+ "severity": 1,
+ "code": 2304,
+ "source": "deno-ts",
+ "message": "Cannot find name 'DuckConfigOptions'.",
+ }],
+ "only": ["quickfix"],
+ },
+ });
+
+ let res =
+ client.write_request("textDocument/codeAction", code_action_params.clone());
+ // Expect single quotes in the auto-import.
+ assert_eq!(
+ res,
+ json!([{
+ "title": "Add import from \"./file01.ts\"",
+ "kind": "quickfix",
+ "diagnostics": [{
+ "range": {
+ "start": { "line": 1, "character": 56 },
+ "end": { "line": 1, "character": 73 },
+ },
+ "severity": 1,
+ "code": 2304,
+ "source": "deno-ts",
+ "message": "Cannot find name 'DuckConfigOptions'.",
+ }],
+ "edit": {
+ "documentChanges": [{
+ "textDocument": {
+ "uri": temp_dir.uri().join("file00.ts").unwrap(),
+ "version": null,
+ },
+ "edits": [{
+ "range": {
+ "start": { "line": 0, "character": 0 },
+ "end": { "line": 0, "character": 0 },
+ },
+ "newText": "import { DuckConfigOptions } from './file01.ts';\n",
+ }],
+ }],
+ },
+ }]),
+ );
+
+ // It should ignore the workspace setting if a `deno.json` is present.
+ temp_dir.write("./deno.json", json!({}).to_string());
+ client.did_change_watched_files(json!({
+ "changes": [{
+ "uri": temp_dir.uri().join("deno.json").unwrap(),
+ "type": 1,
+ }],
+ }));
+
+ let res = client.write_request("textDocument/codeAction", code_action_params);
+ // Expect double quotes in the auto-import.
+ assert_eq!(
+ res,
+ json!([{
+ "title": "Add import from \"./file01.ts\"",
+ "kind": "quickfix",
+ "diagnostics": [{
+ "range": {
+ "start": { "line": 1, "character": 56 },
+ "end": { "line": 1, "character": 73 },
+ },
+ "severity": 1,
+ "code": 2304,
+ "source": "deno-ts",
+ "message": "Cannot find name 'DuckConfigOptions'.",
+ }],
+ "edit": {
+ "documentChanges": [{
+ "textDocument": {
+ "uri": temp_dir.uri().join("file00.ts").unwrap(),
+ "version": null,
+ },
+ "edits": [{
+ "range": {
+ "start": { "line": 0, "character": 0 },
+ "end": { "line": 0, "character": 0 },
+ },
+ "newText": "import { DuckConfigOptions } from \"./file01.ts\";\n",
+ }],
+ }],
+ },
+ }]),
+ );
+}
+
+#[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();