Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions crates/oxc_language_server/src/formatter/server_formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ use crate::{
pub struct ServerFormatterBuilder;

impl ServerFormatterBuilder {
/// # Panics
/// Panics if the root URI cannot be converted to a file path.
pub fn build(root_uri: &Uri, options: serde_json::Value) -> ServerFormatter {
pub fn build(root_uri: &Uri, options: serde_json::Value) -> Option<ServerFormatter> {
let options = match serde_json::from_value::<LSPFormatOptions>(options) {
Ok(opts) => opts,
Err(err) => {
Expand All @@ -34,7 +32,7 @@ impl ServerFormatterBuilder {
}
};

let root_path = root_uri.to_file_path().unwrap();
let root_path = root_uri.to_file_path()?;
let oxfmtrc = Self::get_config(&root_path, options.config_path.as_ref());
let (format_options, oxfmt_options) = Self::get_options(oxfmtrc);

Expand All @@ -49,7 +47,7 @@ impl ServerFormatterBuilder {
}
};

ServerFormatter::new(format_options, gitignore_glob)
Some(ServerFormatter::new(format_options, gitignore_glob))
}
}

Expand All @@ -62,8 +60,8 @@ impl ToolBuilder for ServerFormatterBuilder {
capabilities.document_formatting_provider =
Some(tower_lsp_server::ls_types::OneOf::Left(true));
}
fn build_boxed(&self, root_uri: &Uri, options: serde_json::Value) -> Box<dyn Tool> {
Box::new(ServerFormatterBuilder::build(root_uri, options))
fn build_boxed(&self, root_uri: &Uri, options: serde_json::Value) -> Option<Box<dyn Tool>> {
ServerFormatterBuilder::build(root_uri, options).map(|f| Box::new(f) as Box<dyn Tool>)
}
}

Expand Down Expand Up @@ -162,8 +160,6 @@ impl Tool for ServerFormatter {
fn name(&self) -> &'static str {
"formatter"
}
/// # Panics
/// Panics if the root URI cannot be converted to a file path.
fn handle_configuration_change(
&self,
root_uri: &Uri,
Expand Down Expand Up @@ -196,7 +192,11 @@ impl Tool for ServerFormatter {
return ToolRestartChanges { tool: None, watch_patterns: None };
}

let new_formatter = ServerFormatterBuilder::build(root_uri, new_options_json.clone());
let Some(new_formatter) = ServerFormatterBuilder::build(root_uri, new_options_json.clone())
else {
warn!("Failed to build formatter: root URI cannot be converted to file path");
return ToolRestartChanges { tool: None, watch_patterns: None };
};
let watch_patterns = new_formatter.get_watcher_patterns(new_options_json);
ToolRestartChanges {
tool: Some(Box::new(new_formatter)),
Expand Down Expand Up @@ -230,7 +230,10 @@ impl Tool for ServerFormatter {
) -> ToolRestartChanges {
// TODO: Check if the changed file is actually a config file

let new_formatter = ServerFormatterBuilder::build(root_uri, options);
let Some(new_formatter) = ServerFormatterBuilder::build(root_uri, options) else {
warn!("Failed to build formatter: root URI cannot be converted to file path");
return ToolRestartChanges { tool: None, watch_patterns: None };
};

ToolRestartChanges {
tool: Some(Box::new(new_formatter)),
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_language_server/src/formatter/tester.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ impl Tester<'_> {
&Self::get_root_uri(self.relative_root_dir),
self.options.clone(),
)
.expect("test requires valid root URI")
}

pub fn get_root_uri(relative_root_dir: &str) -> Uri {
Expand Down
30 changes: 19 additions & 11 deletions crates/oxc_language_server/src/linter/server_linter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ use crate::{
pub struct ServerLinterBuilder;

impl ServerLinterBuilder {
/// Build a new `ServerLinter` for the given root URI and options.
///
/// # Panics
/// Panics if the root URI cannot be converted to a file path.
pub fn build(root_uri: &Uri, options: serde_json::Value) -> ServerLinter {
///
/// Panics if an empty `ConfigStoreBuilder` fails to build, which should never happen.
pub fn build(root_uri: &Uri, options: serde_json::Value) -> Option<ServerLinter> {
let options = match serde_json::from_value::<LSPLintOptions>(options) {
Ok(opts) => opts,
Err(e) => {
Expand All @@ -53,7 +56,7 @@ impl ServerLinterBuilder {
LSPLintOptions::default()
}
};
let root_path = root_uri.to_file_path().unwrap();
let root_path = root_uri.to_file_path()?;
let mut nested_ignore_patterns = Vec::new();
let (nested_configs, mut extended_paths) =
Self::create_nested_configs(&root_path, &options, &mut nested_ignore_patterns);
Expand Down Expand Up @@ -133,14 +136,14 @@ impl ServerLinterBuilder {
},
);

ServerLinter::new(
Some(ServerLinter::new(
options.run,
root_path.to_path_buf(),
isolated_linter,
LintIgnoreMatcher::new(&base_patterns, &root_path, nested_ignore_patterns),
Self::create_ignore_glob(&root_path),
extended_paths,
)
))
}
}

Expand Down Expand Up @@ -213,8 +216,8 @@ impl ToolBuilder for ServerLinterBuilder {
Some(DiagnosticServerCapabilities::Options(DiagnosticOptions::default()))
};
}
fn build_boxed(&self, root_uri: &Uri, options: serde_json::Value) -> Box<dyn Tool> {
Box::new(ServerLinterBuilder::build(root_uri, options))
fn build_boxed(&self, root_uri: &Uri, options: serde_json::Value) -> Option<Box<dyn Tool>> {
ServerLinterBuilder::build(root_uri, options).map(|l| Box::new(l) as Box<dyn Tool>)
}
}

Expand Down Expand Up @@ -324,8 +327,6 @@ impl Tool for ServerLinter {
ToolShutdownChanges { uris_to_clear_diagnostics: Some(self.get_cached_uris()) }
}

/// # Panics
/// Panics if the root URI cannot be converted to a file path.
fn handle_configuration_change(
&self,
root_uri: &Uri,
Expand Down Expand Up @@ -357,7 +358,11 @@ impl Tool for ServerLinter {
}

// get the cached files before refreshing the linter, and revalidate them after
let new_linter = ServerLinterBuilder::build(root_uri, new_options_json.clone());
let Some(new_linter) = ServerLinterBuilder::build(root_uri, new_options_json.clone())
else {
warn!("Failed to build linter: root URI cannot be converted to file path");
return ToolRestartChanges { tool: None, watch_patterns: None };
};

let patterns = {
if old_option.config_path == new_options.config_path
Expand Down Expand Up @@ -412,7 +417,10 @@ impl Tool for ServerLinter {
options: serde_json::Value,
) -> ToolRestartChanges {
// TODO: Check if the changed file is actually a config file (including extended paths)
let new_linter = ServerLinterBuilder::build(root_uri, options);
let Some(new_linter) = ServerLinterBuilder::build(root_uri, options) else {
warn!("Failed to build linter: root URI cannot be converted to file path");
return ToolRestartChanges { tool: None, watch_patterns: None };
};

ToolRestartChanges {
tool: Some(Box::new(new_linter)),
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_language_server/src/linter/tester.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ impl Tester<'_> {
&Self::get_root_uri(self.relative_root_dir),
self.options.clone(),
)
.expect("test requires valid root URI")
}

pub fn get_root_uri(relative_root_dir: &str) -> Uri {
Expand Down
8 changes: 4 additions & 4 deletions crates/oxc_language_server/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use crate::{Tool, ToolBuilder, ToolRestartChanges, backend::Backend, tool::Diagn
pub struct FakeToolBuilder;

impl ToolBuilder for FakeToolBuilder {
fn build_boxed(&self, _root_uri: &Uri, _options: serde_json::Value) -> Box<dyn Tool> {
Box::new(FakeTool)
fn build_boxed(&self, _root_uri: &Uri, _options: serde_json::Value) -> Option<Box<dyn Tool>> {
Some(Box::new(FakeTool))
}
}

Expand Down Expand Up @@ -59,7 +59,7 @@ impl Tool for FakeTool {
) -> ToolRestartChanges {
if new_options_json.as_u64() == Some(1) || new_options_json.as_u64() == Some(3) {
return ToolRestartChanges {
tool: Some(FakeToolBuilder.build_boxed(root_uri, new_options_json)),
tool: FakeToolBuilder.build_boxed(root_uri, new_options_json),
watch_patterns: None,
};
}
Expand Down Expand Up @@ -90,7 +90,7 @@ impl Tool for FakeTool {
) -> ToolRestartChanges {
if changed_uri.as_str().ends_with("tool.config") {
return ToolRestartChanges {
tool: Some(FakeToolBuilder.build_boxed(root_uri, options)),
tool: FakeToolBuilder.build_boxed(root_uri, options),
watch_patterns: None,
};
}
Expand Down
3 changes: 2 additions & 1 deletion crates/oxc_language_server/src/tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ pub trait ToolBuilder: Send + Sync {
}

/// Build a boxed instance of the tool for the given root URI and options.
fn build_boxed(&self, root_uri: &Uri, options: serde_json::Value) -> Box<dyn Tool>;
/// Returns `None` if the tool cannot be built for the given root URI and options.
fn build_boxed(&self, root_uri: &Uri, options: serde_json::Value) -> Option<Box<dyn Tool>>;
}

pub type DiagnosticResult = Vec<(Uri, Vec<Diagnostic>)>;
Expand Down
23 changes: 14 additions & 9 deletions crates/oxc_language_server/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,23 @@ impl WorkspaceWorker {
/// e.g. root URI: file:///path/to/root
/// responsible for: file:///path/to/root/file.js
/// not responsible for: file:///path/to/other/file.js
///
/// # Panics
/// Panics if the root URI cannot be converted to a file path.
pub fn is_responsible_for_uri(&self, uri: &Uri) -> bool {
if let Some(path) = uri.to_file_path() {
return path.starts_with(self.root_uri.to_file_path().unwrap());
}
false
let Some(path) = uri.to_file_path() else {
return false;
};
let Some(root_path) = self.root_uri.to_file_path() else {
return false;
};
path.starts_with(root_path)
}

/// Start all programs (linter, formatter) for the worker.
/// This should be called after the client has sent the workspace configuration.
pub async fn start_worker(&self, options: serde_json::Value, tools: &[Box<dyn ToolBuilder>]) {
*self.tools.write().await =
tools.iter().map(|tool| tool.build_boxed(&self.root_uri, options.clone())).collect();
*self.tools.write().await = tools
.iter()
.filter_map(|tool| tool.build_boxed(&self.root_uri, options.clone()))
.collect();

*self.options.lock().await = Some(options);
}
Expand Down Expand Up @@ -428,6 +430,9 @@ mod tests {
!worker
.is_responsible_for_uri(&Uri::from_str("file:///path/to/other/file.js").unwrap())
);
assert!(!worker.is_responsible_for_uri(
&Uri::from_str("file:///path/to/root_second/file.js").unwrap()
));
}

#[tokio::test]
Expand Down
Loading