Skip to content

Commit 8336b32

Browse files
authored
Merge pull request #865 from posit-dev/task/refactor-config
Simplify LSP settings
2 parents dad2c39 + eccee92 commit 8336b32

File tree

6 files changed

+136
-249
lines changed

6 files changed

+136
-249
lines changed

.github/copilot-instructions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ judgement to produce the clearest code organization.
2222

2323
Keep the main logic as unnested as possible. Favour Rust's `let ... else`
2424
syntax to return early or continue a loop in the `else` clause, over `if let`.
25+
26+
Always prefer importing with `use` instead of qualifying with `::`, unless specifically requested in these instructions or by the user, or you see existing `::` usages in the file you're editing.

Cargo.lock

Lines changed: 0 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ark/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ url = "2.4.1"
5353
walkdir = "2"
5454
yaml-rust = "0.4.5"
5555
winsafe = { version = "0.0.19", features = ["kernel"] }
56-
struct-field-names-as-array = "0.3.0"
5756
strum = "0.26.2"
5857
strum_macros = "0.26.2"
5958
futures = "0.3.30"

crates/ark/src/lsp/config.rs

Lines changed: 70 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,77 @@
11
use serde::Deserialize;
22
use serde::Serialize;
3-
use struct_field_names_as_array::FieldNamesAsArray;
3+
use serde_json::Value;
44

5-
use crate::lsp;
65
use crate::lsp::diagnostics::DiagnosticsConfig;
76

7+
pub struct Setting<T> {
8+
pub key: &'static str,
9+
pub set: fn(&mut T, Value),
10+
}
11+
12+
/// List of LSP settings for which clients can send `didChangeConfiguration`
13+
/// notifications. We register our interest in watching over these settings in
14+
/// our `initialized` handler. The `set` methods convert from a json `Value` to
15+
/// the expected type, using a default value if the conversion fails.
16+
///
17+
/// This array is for global settings. If the setting should only affect a given
18+
/// document URI, add it to `DOCUMENT_SETTINGS` instead.
19+
pub static GLOBAL_SETTINGS: &[Setting<LspConfig>] = &[
20+
Setting {
21+
key: "positron.r.diagnostics.enable",
22+
set: |cfg, v| {
23+
cfg.diagnostics.enable = v
24+
.as_bool()
25+
.unwrap_or_else(|| DiagnosticsConfig::default().enable)
26+
},
27+
},
28+
Setting {
29+
key: "positron.r.symbols.includeAssignmentsInBlocks",
30+
set: |cfg, v| {
31+
cfg.symbols.include_assignments_in_blocks = v
32+
.as_bool()
33+
.unwrap_or_else(|| SymbolsConfig::default().include_assignments_in_blocks)
34+
},
35+
},
36+
];
37+
38+
/// These document settings are updated on a URI basis. Each document has its
39+
/// own value of the setting.
40+
pub static DOCUMENT_SETTINGS: &[Setting<DocumentConfig>] = &[
41+
Setting {
42+
key: "editor.insertSpaces",
43+
set: |cfg, v| {
44+
let default_style = IndentationConfig::default().indent_style;
45+
cfg.indent.indent_style = if v
46+
.as_bool()
47+
.unwrap_or_else(|| default_style == IndentStyle::Space)
48+
{
49+
IndentStyle::Space
50+
} else {
51+
IndentStyle::Tab
52+
}
53+
},
54+
},
55+
Setting {
56+
key: "editor.indentSize",
57+
set: |cfg, v| {
58+
cfg.indent.indent_size = v
59+
.as_u64()
60+
.map(|n| n as usize)
61+
.unwrap_or_else(|| IndentationConfig::default().indent_size)
62+
},
63+
},
64+
Setting {
65+
key: "editor.tabSize",
66+
set: |cfg, v| {
67+
cfg.indent.tab_width = v
68+
.as_u64()
69+
.map(|n| n as usize)
70+
.unwrap_or_else(|| IndentationConfig::default().tab_width)
71+
},
72+
},
73+
];
74+
875
/// Configuration of the LSP
976
#[derive(Clone, Default, Debug)]
1077
pub(crate) struct LspConfig {
@@ -39,40 +106,12 @@ pub struct IndentationConfig {
39106
pub tab_width: usize,
40107
}
41108

42-
#[derive(Serialize, Deserialize, Clone, Debug)]
109+
#[derive(PartialEq, Serialize, Deserialize, Clone, Debug)]
43110
pub enum IndentStyle {
44111
Tab,
45112
Space,
46113
}
47114

48-
/// VS Code representation of a document configuration
49-
#[derive(Serialize, Deserialize, FieldNamesAsArray, Clone, Debug)]
50-
pub(crate) struct VscDocumentConfig {
51-
// DEV NOTE: Update `section_from_key()` method after adding a field
52-
pub insert_spaces: bool,
53-
pub indent_size: VscIndentSize,
54-
pub tab_size: usize,
55-
}
56-
57-
#[derive(Serialize, Deserialize, FieldNamesAsArray, Clone, Debug)]
58-
pub(crate) struct VscDiagnosticsConfig {
59-
// DEV NOTE: Update `section_from_key()` method after adding a field
60-
pub enable: bool,
61-
}
62-
63-
#[derive(Serialize, Deserialize, FieldNamesAsArray, Clone, Debug)]
64-
pub(crate) struct VscSymbolsConfig {
65-
// DEV NOTE: Update `section_from_key()` method after adding a field
66-
pub include_assignments_in_blocks: bool,
67-
}
68-
69-
#[derive(Serialize, Deserialize, Clone, Debug)]
70-
#[serde(untagged)]
71-
pub(crate) enum VscIndentSize {
72-
Alias(String),
73-
Size(usize),
74-
}
75-
76115
impl Default for SymbolsConfig {
77116
fn default() -> Self {
78117
Self {
@@ -91,79 +130,6 @@ impl Default for IndentationConfig {
91130
}
92131
}
93132

94-
impl VscDocumentConfig {
95-
pub(crate) fn section_from_key(key: &str) -> &str {
96-
match key {
97-
"insert_spaces" => "editor.insertSpaces",
98-
"indent_size" => "editor.indentSize",
99-
"tab_size" => "editor.tabSize",
100-
_ => "unknown", // To be caught via downstream errors
101-
}
102-
}
103-
}
104-
105-
/// Convert from VS Code representation of a document config to our own
106-
/// representation. Currently one-to-one.
107-
impl From<VscDocumentConfig> for DocumentConfig {
108-
fn from(x: VscDocumentConfig) -> Self {
109-
let indent_style = indent_style_from_lsp(x.insert_spaces);
110-
111-
let indent_size = match x.indent_size {
112-
VscIndentSize::Size(size) => size,
113-
VscIndentSize::Alias(var) => {
114-
if var == "tabSize" {
115-
x.tab_size
116-
} else {
117-
lsp::log_warn!("Unknown indent alias {var}, using default");
118-
2
119-
}
120-
},
121-
};
122-
123-
Self {
124-
indent: IndentationConfig {
125-
indent_style,
126-
indent_size,
127-
tab_width: x.tab_size,
128-
},
129-
}
130-
}
131-
}
132-
133-
impl VscDiagnosticsConfig {
134-
pub(crate) fn section_from_key(key: &str) -> &str {
135-
match key {
136-
"enable" => "positron.r.diagnostics.enable",
137-
_ => "unknown", // To be caught via downstream errors
138-
}
139-
}
140-
}
141-
142-
impl From<VscDiagnosticsConfig> for DiagnosticsConfig {
143-
fn from(value: VscDiagnosticsConfig) -> Self {
144-
Self {
145-
enable: value.enable,
146-
}
147-
}
148-
}
149-
150-
impl VscSymbolsConfig {
151-
pub(crate) fn section_from_key(key: &str) -> &str {
152-
match key {
153-
"include_assignments_in_blocks" => "positron.r.symbols.includeAssignmentsInBlocks",
154-
_ => "unknown", // To be caught via downstream errors
155-
}
156-
}
157-
}
158-
159-
impl From<VscSymbolsConfig> for SymbolsConfig {
160-
fn from(value: VscSymbolsConfig) -> Self {
161-
Self {
162-
include_assignments_in_blocks: value.include_assignments_in_blocks,
163-
}
164-
}
165-
}
166-
167133
pub(crate) fn indent_style_from_lsp(insert_spaces: bool) -> IndentStyle {
168134
if insert_spaces {
169135
IndentStyle::Space

crates/ark/src/lsp/handlers.rs

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use anyhow::anyhow;
99
use serde_json::Value;
1010
use stdext::unwrap;
1111
use stdext::unwrap::IntoResult;
12-
use struct_field_names_as_array::FieldNamesAsArray;
1312
use tower_lsp::lsp_types::CodeActionParams;
1413
use tower_lsp::lsp_types::CodeActionResponse;
1514
use tower_lsp::lsp_types::CompletionItem;
@@ -46,9 +45,6 @@ use crate::lsp;
4645
use crate::lsp::code_action::code_actions;
4746
use crate::lsp::completions::provide_completions;
4847
use crate::lsp::completions::resolve_completion;
49-
use crate::lsp::config::VscDiagnosticsConfig;
50-
use crate::lsp::config::VscDocumentConfig;
51-
use crate::lsp::config::VscSymbolsConfig;
5248
use crate::lsp::definitions::goto_definition;
5349
use crate::lsp::document_context::DocumentContext;
5450
use crate::lsp::encoding::convert_lsp_range_to_tree_sitter_range;
@@ -106,22 +102,21 @@ pub(crate) async fn handle_initialized(
106102
// Note that some settings, such as editor indentation properties, may be
107103
// changed by extensions or by the user without changing the actual
108104
// underlying setting. Unfortunately we don't receive updates in that case.
109-
let mut config_document_regs = collect_regs(
110-
VscDocumentConfig::FIELD_NAMES_AS_ARRAY.to_vec(),
111-
VscDocumentConfig::section_from_key,
112-
);
113-
let mut config_symbols_regs: Vec<Registration> = collect_regs(
114-
VscSymbolsConfig::FIELD_NAMES_AS_ARRAY.to_vec(),
115-
VscSymbolsConfig::section_from_key,
116-
);
117-
let mut config_diagnostics_regs: Vec<Registration> = collect_regs(
118-
VscDiagnosticsConfig::FIELD_NAMES_AS_ARRAY.to_vec(),
119-
VscDiagnosticsConfig::section_from_key,
120-
);
121-
122-
regs.append(&mut config_document_regs);
123-
regs.append(&mut config_symbols_regs);
124-
regs.append(&mut config_diagnostics_regs);
105+
106+
for setting in crate::lsp::config::GLOBAL_SETTINGS {
107+
regs.push(Registration {
108+
id: uuid::Uuid::new_v4().to_string(),
109+
method: String::from("workspace/didChangeConfiguration"),
110+
register_options: Some(serde_json::json!({ "section": setting.key })),
111+
});
112+
}
113+
for setting in crate::lsp::config::DOCUMENT_SETTINGS {
114+
regs.push(Registration {
115+
id: uuid::Uuid::new_v4().to_string(),
116+
method: String::from("workspace/didChangeConfiguration"),
117+
register_options: Some(serde_json::json!({ "section": setting.key })),
118+
});
119+
}
125120
}
126121

127122
client
@@ -131,17 +126,6 @@ pub(crate) async fn handle_initialized(
131126
Ok(())
132127
}
133128

134-
fn collect_regs(fields: Vec<&str>, into_section: impl Fn(&str) -> &str) -> Vec<Registration> {
135-
fields
136-
.into_iter()
137-
.map(|field| Registration {
138-
id: uuid::Uuid::new_v4().to_string(),
139-
method: String::from("workspace/didChangeConfiguration"),
140-
register_options: Some(serde_json::json!({ "section": into_section(field) })),
141-
})
142-
.collect()
143-
}
144-
145129
#[tracing::instrument(level = "info", skip_all)]
146130
pub(crate) fn handle_symbol(
147131
params: WorkspaceSymbolParams,

0 commit comments

Comments
 (0)