feat: preconfigure Python with Astral ty and Ruff
This commit is contained in:
+226
-5
@@ -981,9 +981,9 @@ end"#,
|
||||
end"#,
|
||||
);
|
||||
let config = load_config(&path).unwrap();
|
||||
assert_eq!(config.lsp.len(), 4);
|
||||
for (language, command, extension) in [
|
||||
("rust", &["rust-analyzer"][..], "rs"),
|
||||
("python", &["pylsp"][..], "py"),
|
||||
("ruby", &["solargraph", "stdio"][..], "rb"),
|
||||
] {
|
||||
let server = config
|
||||
@@ -996,6 +996,25 @@ end"#,
|
||||
assert_eq!(server.initialization_options, serde_json::json!({}));
|
||||
assert_eq!(server.settings, serde_json::json!({}));
|
||||
}
|
||||
let python: Vec<_> = config
|
||||
.lsp
|
||||
.iter()
|
||||
.filter(|server| server.language_id == "python")
|
||||
.collect();
|
||||
assert_eq!(python.len(), 2);
|
||||
for (server, command, initialization_options) in [
|
||||
(
|
||||
python[0],
|
||||
["ty", "server"],
|
||||
serde_json::json!({"experimental": {"useUv": "off"}}),
|
||||
),
|
||||
(python[1], ["ruff", "server"], serde_json::json!({})),
|
||||
] {
|
||||
assert_eq!(server.command, command);
|
||||
assert_eq!(server.extensions, ["py"]);
|
||||
assert_eq!(server.initialization_options, initialization_options);
|
||||
assert_eq!(server.settings, serde_json::json!({}));
|
||||
}
|
||||
|
||||
let path = fixture.file(
|
||||
"config.rb",
|
||||
@@ -1005,7 +1024,7 @@ end"#,
|
||||
initialization_options: {cargo: {features: ["α"]}},
|
||||
settings: {"rust-analyzer" => {check: {command: "clippy"}}}
|
||||
}
|
||||
config.lsp["python"] = {command: ["custom pylsp", "--stdio"]}
|
||||
config.lsp["python"] = {command: ["custom-python-server", "--stdio"]}
|
||||
config.lsp["ruby"] = {command: ["bundle", "exec", "solargraph", "stdio"]}
|
||||
config.lsp["my-language.c++"] = {
|
||||
command: ["custom server", "--flag", ""],
|
||||
@@ -1035,10 +1054,18 @@ end"#,
|
||||
.iter()
|
||||
.find(|server| server.language_id == "python")
|
||||
.unwrap();
|
||||
assert_eq!(python.command, ["custom pylsp", "--stdio"]);
|
||||
assert_eq!(python.command, ["custom-python-server", "--stdio"]);
|
||||
assert_eq!(python.extensions, ["py"]);
|
||||
assert_eq!(python.initialization_options, serde_json::json!({}));
|
||||
assert_eq!(python.settings, serde_json::json!({}));
|
||||
assert_eq!(
|
||||
config
|
||||
.lsp
|
||||
.iter()
|
||||
.filter(|server| server.language_id == "python")
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
let ruby = config
|
||||
.lsp
|
||||
.iter()
|
||||
@@ -1060,6 +1087,182 @@ end"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_python_common_options_and_ordered_server_overrides() {
|
||||
let fixture = Fixture::new();
|
||||
let path = fixture.file(
|
||||
"config.rb",
|
||||
r#"Vamp.configure do |config|
|
||||
config.lsp["python"] = {
|
||||
extensions: ["py", "pyi"],
|
||||
initialization_options: {diagnosticMode: "workspace"},
|
||||
settings: {shared: true}
|
||||
}
|
||||
end"#,
|
||||
);
|
||||
let config = load_config(&path).unwrap();
|
||||
assert_eq!(config.lsp.len(), 2);
|
||||
for (server, command) in config
|
||||
.lsp
|
||||
.iter()
|
||||
.zip([["ty", "server"], ["ruff", "server"]])
|
||||
{
|
||||
assert_eq!(server.language_id, "python");
|
||||
assert_eq!(server.command, command);
|
||||
assert_eq!(server.extensions, ["py", "pyi"]);
|
||||
assert_eq!(
|
||||
server.initialization_options,
|
||||
serde_json::json!({"diagnosticMode": "workspace"})
|
||||
);
|
||||
assert_eq!(server.settings, serde_json::json!({"shared": true}));
|
||||
}
|
||||
let path = fixture.file(
|
||||
"config.rb",
|
||||
r#"Vamp.configure do |config|
|
||||
config.lsp["python"] = {servers: [
|
||||
{command: ["type-server", "--stdio"], initialization_options: {experimental: {useUv: "off"}}, settings: {first: true}},
|
||||
{command: ["lint-server"], initialization_options: {settings: {lint: {enable: true}}}}
|
||||
]}
|
||||
config.lsp["custom"] = {extensions: ["custom"], servers: [
|
||||
{command: ["custom-first"]}, {command: ["custom-second"], settings: {second: true}}
|
||||
]}
|
||||
end"#,
|
||||
);
|
||||
let config = load_config(&path).unwrap();
|
||||
for (language, commands, extensions) in [
|
||||
(
|
||||
"python",
|
||||
[&["type-server", "--stdio"][..], &["lint-server"][..]],
|
||||
["py"],
|
||||
),
|
||||
(
|
||||
"custom",
|
||||
[&["custom-first"][..], &["custom-second"][..]],
|
||||
["custom"],
|
||||
),
|
||||
] {
|
||||
let servers: Vec<_> = config
|
||||
.lsp
|
||||
.iter()
|
||||
.filter(|server| server.language_id == language)
|
||||
.collect();
|
||||
assert_eq!(servers.len(), 2);
|
||||
for (server, command) in servers.iter().zip(commands) {
|
||||
assert_eq!(server.command, command);
|
||||
assert_eq!(server.extensions, extensions);
|
||||
}
|
||||
}
|
||||
let python: Vec<_> = config
|
||||
.lsp
|
||||
.iter()
|
||||
.filter(|server| server.language_id == "python")
|
||||
.collect();
|
||||
assert_eq!(
|
||||
python[0].initialization_options,
|
||||
serde_json::json!({"experimental": {"useUv": "off"}})
|
||||
);
|
||||
assert_eq!(python[0].settings, serde_json::json!({"first": true}));
|
||||
assert_eq!(
|
||||
python[1].initialization_options,
|
||||
serde_json::json!({"settings": {"lint": {"enable": true}}})
|
||||
);
|
||||
assert_eq!(python[1].settings, serde_json::json!({}));
|
||||
let custom: Vec<_> = config
|
||||
.lsp
|
||||
.iter()
|
||||
.filter(|server| server.language_id == "custom")
|
||||
.collect();
|
||||
assert_eq!(custom[0].initialization_options, serde_json::json!({}));
|
||||
assert_eq!(custom[1].settings, serde_json::json!({"second": true}));
|
||||
let path = fixture.file(
|
||||
"config.rb",
|
||||
r#"Vamp.configure { |config| config.lsp["python"] = {settings: {shared: true}} }"#,
|
||||
);
|
||||
let config = load_config(&path).unwrap();
|
||||
assert_eq!(
|
||||
config.lsp[0].initialization_options,
|
||||
serde_json::json!({"experimental": {"useUv": "off"}})
|
||||
);
|
||||
assert_eq!(config.lsp[1].initialization_options, serde_json::json!({}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_server_lists_reject_invalid_shapes_and_ambiguous_options_locally() {
|
||||
let fixture = Fixture::new();
|
||||
for (options, error) in [
|
||||
("{servers: nil}", "nonempty array"),
|
||||
("{servers: {command: ['server']}}", "nonempty array"),
|
||||
("{servers: []}", "nonempty array"),
|
||||
(
|
||||
"{servers: [true]}",
|
||||
"servers[0] must be a configuration object",
|
||||
),
|
||||
("{servers: [{}]}", "servers[0] requires command argv"),
|
||||
("{servers: [{command: []}]}", "servers[0] command must be"),
|
||||
(
|
||||
"{servers: [{command: ['server'], extensions: ['py']}]}",
|
||||
"servers[0] has unknown option",
|
||||
),
|
||||
(
|
||||
"{servers: [{command: ['server'], servers: []}]}",
|
||||
"servers[0] has unknown option",
|
||||
),
|
||||
(
|
||||
"{servers: [{command: ['server'], initialization_options: []}]}",
|
||||
"servers[0] initialization_options must be",
|
||||
),
|
||||
(
|
||||
"{servers: [{command: ['server']}, {command: ['other'], settings: nil}]}",
|
||||
"servers[1] settings must be",
|
||||
),
|
||||
(
|
||||
"{servers: [{command: ['server']}], command: ['other']}",
|
||||
"cannot be combined",
|
||||
),
|
||||
(
|
||||
"{servers: [{command: ['server']}], initialization_options: {}}",
|
||||
"cannot be combined",
|
||||
),
|
||||
(
|
||||
"{servers: [{command: ['server']}], settings: {}}",
|
||||
"cannot be combined",
|
||||
),
|
||||
] {
|
||||
let path = fixture.file(
|
||||
"config.rb",
|
||||
&format!("Vamp.configure {{ |config| config.lsp[\"python\"] = {options} }}"),
|
||||
);
|
||||
let actual = load_config(&path).unwrap_err();
|
||||
assert!(
|
||||
actual.starts_with("Ruby config: LSP language \"python\""),
|
||||
"{actual}"
|
||||
);
|
||||
assert!(actual.contains(error), "{options}: {actual}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_server_limit_is_enforced_at_configuration_startup() {
|
||||
let fixture = Fixture::new();
|
||||
for count in [16, 17] {
|
||||
let path = fixture.file(
|
||||
"config.rb",
|
||||
&format!(
|
||||
"Vamp.configure {{ |config| config.lsp[\"python\"] = {{servers: Array.new({count}) {{ {{command: [\"server\"]}} }} }} }}"
|
||||
),
|
||||
);
|
||||
let result = load_config(&path);
|
||||
if count == 16 {
|
||||
assert_eq!(result.unwrap().lsp.len(), 16);
|
||||
} else {
|
||||
assert_eq!(
|
||||
result.unwrap_err(),
|
||||
"Ruby config: LSP configuration supports at most 16 configured servers"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_configuration_rejects_invalid_keys_values_and_json_options() {
|
||||
let fixture = Fixture::new();
|
||||
@@ -1074,6 +1277,7 @@ end"#,
|
||||
r#"config.lsp["rust"] = "yes""#,
|
||||
r#"config.lsp["custom"] = {}"#,
|
||||
r#"config.lsp["custom"] = {command: ["server"]}"#,
|
||||
r#"config.lsp["custom"] = {servers: [{command: ["server"]}]}"#,
|
||||
r#"config.lsp["custom"] = {extensions: ["custom"]}"#,
|
||||
r#"config.lsp["rust"] = {command: "server"}"#,
|
||||
r#"config.lsp["rust"] = {command: []}"#,
|
||||
@@ -1146,6 +1350,7 @@ end"#,
|
||||
serde_json::json!({"": false}),
|
||||
serde_json::json!({"bad\u{0085}key": false}),
|
||||
serde_json::json!({"rust": null}),
|
||||
serde_json::json!({"python": []}),
|
||||
serde_json::json!({"unknown": true}),
|
||||
serde_json::json!({"custom": {}}),
|
||||
serde_json::json!({"rust": {"command": ["tool", "bad\0argument"]}}),
|
||||
@@ -1153,6 +1358,11 @@ end"#,
|
||||
serde_json::json!({"rust": {"initialization_options": []}}),
|
||||
serde_json::json!({"rust": {"settings": "not-an-object"}}),
|
||||
serde_json::json!({"rust": {"unexpected": true}}),
|
||||
serde_json::json!({"python": {"servers": []}}),
|
||||
serde_json::json!({"python": {"servers": [null]}}),
|
||||
serde_json::json!({"python": {"servers": [{}]}}),
|
||||
serde_json::json!({"python": {"servers": [{"command": ["server"], "extensions": ["py"]}]}}),
|
||||
serde_json::json!({"python": {"servers": [{"command": ["server"]}], "settings": {}}}),
|
||||
] {
|
||||
frame["lsp"] = value;
|
||||
let Response::Config { lsp, .. } =
|
||||
@@ -1162,7 +1372,7 @@ end"#,
|
||||
};
|
||||
assert!(crate::lsp_config::resolve(lsp).is_err(), "{frame}");
|
||||
}
|
||||
let languages: serde_json::Map<String, serde_json::Value> = (0..17)
|
||||
let mut languages: serde_json::Map<String, serde_json::Value> = (0..14)
|
||||
.map(|index| {
|
||||
(
|
||||
format!("custom-{index}"),
|
||||
@@ -1170,13 +1380,24 @@ end"#,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
languages.insert("python".into(), serde_json::json!(true));
|
||||
frame["lsp"] = serde_json::Value::Object(languages);
|
||||
let Response::Config { lsp, .. } =
|
||||
serde_json::from_value::<Response>(frame.clone()).unwrap()
|
||||
else {
|
||||
panic!("expected config frame");
|
||||
};
|
||||
assert!(crate::lsp_config::resolve(lsp).is_err());
|
||||
assert_eq!(crate::lsp_config::resolve(lsp).unwrap().len(), 16);
|
||||
frame["lsp"]["ruby"] = serde_json::json!(true);
|
||||
let Response::Config { lsp, .. } =
|
||||
serde_json::from_value::<Response>(frame.clone()).unwrap()
|
||||
else {
|
||||
panic!("expected config frame");
|
||||
};
|
||||
assert_eq!(
|
||||
crate::lsp_config::resolve(lsp).unwrap_err(),
|
||||
"LSP configuration supports at most 16 configured servers"
|
||||
);
|
||||
frame.as_object_mut().unwrap().remove("lsp");
|
||||
assert!(serde_json::from_value::<Response>(frame).is_err());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user